diff --git a/.changeset/beige-poets-move.md b/.changeset/beige-poets-move.md new file mode 100644 index 0000000000..476fc3bf21 --- /dev/null +++ b/.changeset/beige-poets-move.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Nested mutation operations now follow the relationship direction behaviour as defined in `queryDirection` diff --git a/.changeset/calm-balloons-argue.md b/.changeset/calm-balloons-argue.md new file mode 100644 index 0000000000..50aa44daac --- /dev/null +++ b/.changeset/calm-balloons-argue.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove `publish` method from `Neo4jGraphQLSubscriptionsEngine` interface as it is no longer used with CDC-based subscriptions. Implementing this method on custom engines will no longer have an effect, and it is no longer possible to call `publish` directly on `Neo4jGraphQLSubscriptionsCDCEngine` diff --git a/.changeset/chatty-plants-dress.md b/.changeset/chatty-plants-dress.md new file mode 100644 index 0000000000..1bc68a9c29 --- /dev/null +++ b/.changeset/chatty-plants-dress.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove `@private` directive. This directive was intended to be used with the library `@neo4j/graphql-ogm` which is no longer supported. diff --git a/.changeset/clean-hairs-pretend.md b/.changeset/clean-hairs-pretend.md new file mode 100644 index 0000000000..0f3dc571d5 --- /dev/null +++ b/.changeset/clean-hairs-pretend.md @@ -0,0 +1,15 @@ +--- +"@neo4j/graphql": major +--- + +Fails schema generation if there are conflicting plural names in types. For example, the following schema will fail, due to ambiguous `Techs` plural + +```graphql +type Tech @node(plural: "Techs") { + name: String +} + +type Techs { + value: String +} +``` diff --git a/.changeset/clever-tomatoes-float.md b/.changeset/clever-tomatoes-float.md new file mode 100644 index 0000000000..811abd99bd --- /dev/null +++ b/.changeset/clever-tomatoes-float.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +The `typename_IN` filter has been renamed to `typename`. diff --git a/.changeset/cyan-grapes-laugh.md b/.changeset/cyan-grapes-laugh.md new file mode 100644 index 0000000000..41fd62574b --- /dev/null +++ b/.changeset/cyan-grapes-laugh.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove support for `connectOrCreate` operations diff --git a/.changeset/fair-elephants-yell.md b/.changeset/fair-elephants-yell.md new file mode 100644 index 0000000000..8c7ab0dc26 --- /dev/null +++ b/.changeset/fair-elephants-yell.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Aggregations are no longer generated for `ID` fields. diff --git a/.changeset/few-sloths-battle.md b/.changeset/few-sloths-battle.md new file mode 100644 index 0000000000..f302ec4d5d --- /dev/null +++ b/.changeset/few-sloths-battle.md @@ -0,0 +1,7 @@ +--- +"@neo4j/graphql": major +--- + +Changes the result projection where there are multiple relationships between two nodes. + +In the case of using the connection API then multiple relationships will still be represented, as there is the ability to select the relationship properties. In the non-connection API case, the duplicate results will only return distinct results. diff --git a/.changeset/four-insects-repeat.md b/.changeset/four-insects-repeat.md new file mode 100644 index 0000000000..93f9df5278 --- /dev/null +++ b/.changeset/four-insects-repeat.md @@ -0,0 +1,15 @@ +--- +"@neo4j/graphql": patch +--- + +Deprecate individual mutations in favor of generic mutations + +- `_SET` +- `_POP` +- `_PUSH` +- `_INCREMENT` +- `_ADD` +- `_DECREMENT` +- `_SUBTRACT` +- `_MULTIPLY` +- `_DIVIDE` diff --git a/.changeset/good-sheep-refuse.md b/.changeset/good-sheep-refuse.md new file mode 100644 index 0000000000..a2359ba828 --- /dev/null +++ b/.changeset/good-sheep-refuse.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +`DateTime` and `Time` values are now converted from strings into temporal types in the generated Cypher instead of in server code using the driver. This could result in different values when the database is in a different timezone to the GraphQL server. diff --git a/.changeset/green-jobs-jam.md b/.changeset/green-jobs-jam.md new file mode 100644 index 0000000000..8221a8db07 --- /dev/null +++ b/.changeset/green-jobs-jam.md @@ -0,0 +1,16 @@ +--- +"@neo4j/graphql": minor +--- + +Add suport for generic update operators: + +```graphql +mutation { + updateMovies(update: { name: { set: "The Matrix" } }) { + movies { + id + name + } + } +} +``` diff --git a/.changeset/healthy-swans-shave.md b/.changeset/healthy-swans-shave.md new file mode 100644 index 0000000000..1fc4dac9b1 --- /dev/null +++ b/.changeset/healthy-swans-shave.md @@ -0,0 +1,7 @@ +--- +"@neo4j/graphql": major +--- + +The deprecated `directed` argument has been removed, and `queryDirection` now only accepts two possible values - `DIRECTED` (default) and `UNDIRECTED`. + +Additionally, the `directedArgument` setting of `excludeDeprecatedFields` has been removed as these deprecated fields have been removed. diff --git a/.changeset/khaki-roses-raise.md b/.changeset/khaki-roses-raise.md new file mode 100644 index 0000000000..1c14bed120 --- /dev/null +++ b/.changeset/khaki-roses-raise.md @@ -0,0 +1,31 @@ +--- +"@neo4j/graphql": patch +--- + +Deprecates old aggregation filters for relationships in favor of more generic filters: + +Before: + +```js +query Movies { + movies( + where: { actorsAggregate: { node: { lastRating_AVERAGE_GT: 6 } } } + ) { + title + } +} +``` + +Now: + +```js +query Movies { + movies( + where: { + actorsAggregate: { node: { lastRating: { average: { gt: 6 } } } } + } + ) { + title + } +} +``` diff --git a/.changeset/kind-clocks-tickle.md b/.changeset/kind-clocks-tickle.md new file mode 100644 index 0000000000..034bc44340 --- /dev/null +++ b/.changeset/kind-clocks-tickle.md @@ -0,0 +1,34 @@ +--- +"@neo4j/graphql": major +--- + +Sets addVersionPrefix to true by default, this will prepend the Cypher version to all queries by default, ensuring that the correct Cypher version is used in Neo4j: + +```cypher +CYPHER 5 +MATCH(this:Movie) +``` + +This may be incompatible with older versions of Neo4j and can be disabled by setting `cypherQueryOption.addVersionPrefix` in the context to false: + +```js +{ + cypherQueryOptions: { + addVersionPrefix: true, + }, +} +``` + +For example, for an apollo server: + +```js +await startStandaloneServer(server, { + context: async ({ req }) => ({ + req, + cypherQueryOptions: { + addVersionPrefix: false, + }, + }), + listen: { port: 4000 }, +}); +``` diff --git a/.changeset/little-lemons-fail.md b/.changeset/little-lemons-fail.md new file mode 100644 index 0000000000..b6f00f7705 --- /dev/null +++ b/.changeset/little-lemons-fail.md @@ -0,0 +1,38 @@ +--- +"@neo4j/graphql": major +--- + +Single element relationships have been removed in favor of list relationships: + +Before + +```graphql +type Movie { + director: Person @relationship(type: "DIRECTED", direction: "IN") +} +``` + +After + +```graphql +type Movie { + director: [Person!]! @relationship(type: "DIRECTED", direction: "IN") +} +``` + +This requires updating filters, clients and auth rules to use the list filter operations. + +Single element relationships cannot be reliably enforced, leading to a data inconsistent with the schema. If the GraphQL model requires 1-1 relationships (such as in federations) these can now be achieved with the `@cypher` directive instead: + +```graphql +type Movie { + director: Person + @cypher( + statement: """ + MATCH(this)-[:ACTED_IN]->(p:Person) + RETURN p + """ + columnName: "p" + ) +} +``` diff --git a/.changeset/loud-phones-march.md b/.changeset/loud-phones-march.md new file mode 100644 index 0000000000..7c45f04987 --- /dev/null +++ b/.changeset/loud-phones-march.md @@ -0,0 +1,69 @@ +--- +"@neo4j/graphql": major +--- + +There have been major changes to the way that full-text search operates. + +The directive now requires the specification of an index name, query name, and indexed fields. + +```graphql +input FulltextInput { + indexName: String! + queryName: String! + fields: [String]! +} + +""" +Informs @neo4j/graphql that there should be a fulltext index in the database, allows users to search by the index in the generated schema. +""" +directive @fulltext(indexes: [FulltextInput]!) on OBJECT +``` + +Here is an example of how this might be used: + +```graphql +type Movie @node @fulltext(indexName: "movieTitleIndex", queryName: "moviesByTitle", fields: ["title"]) { + title: String! +} +``` + +Full-text search was previously available in two different locations. + +The following form has now been completely removed: + +```graphql +# Removed +{ + movies(fulltext: { movieTitleIndex: { phrase: "The Matrix" } }) { + title + } +} +``` + +The following form as a root-level query has been changed: + +```graphql +# Old query +query { + moviesByTitle(phrase: "The Matrix") { + score + movies { + title + } + } +} + +# New query +query { + moviesByTitle(phrase: "The Matrix") { + edges { + score + node { + title + } + } + } +} +``` + +The new form is as a Relay connection, which allows for pagination using cursors and access to the `pageInfo` field. diff --git a/.changeset/nine-games-clap.md b/.changeset/nine-games-clap.md new file mode 100644 index 0000000000..176357f8e9 --- /dev/null +++ b/.changeset/nine-games-clap.md @@ -0,0 +1,6 @@ +--- +"@neo4j/graphql": patch +--- + +Deprecate relationship filtering using the non-generic version such as `actors_SOME: { title_EQ: "The Matrix" }` in favor of the generic input `actors: { some: { title: { eq: "The Matrix" } } }`. +The setting `excludeDeprecatedFields` now contains the option `relationshipFilters` to remove these deprecated filters. diff --git a/.changeset/perfect-zoos-push.md b/.changeset/perfect-zoos-push.md new file mode 100644 index 0000000000..a73f35134c --- /dev/null +++ b/.changeset/perfect-zoos-push.md @@ -0,0 +1,6 @@ +--- +"@neo4j/graphql": major +--- + +Change the way how `@node` behaves, `@node` is now required, and GraphQL Object types without the directive `@node` will no longer considered as a Neo4j Nodes representation. +Queries and Mutations will be generated only for types with the `@node` directive. diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..d5d1152e83 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,45 @@ +{ + "mode": "pre", + "tag": "alpha", + "initialVersions": { + "apollo-federation-subgraph-compatibility": "1.0.0", + "@neo4j/graphql": "6.2.1", + "server_perf": "1.0.0", + "@neo4j/introspector": "4.0.0", + "@neo4j/package-tests": "1.0.0" + }, + "changesets": [ + "beige-poets-move", + "calm-balloons-argue", + "chatty-plants-dress", + "clean-hairs-pretend", + "clever-tomatoes-float", + "cyan-grapes-laugh", + "fair-elephants-yell", + "few-sloths-battle", + "four-insects-repeat", + "good-sheep-refuse", + "green-jobs-jam", + "healthy-swans-shave", + "khaki-roses-raise", + "kind-clocks-tickle", + "little-lemons-fail", + "loud-phones-march", + "nine-games-clap", + "perfect-zoos-push", + "quick-dolphins-accept", + "red-cows-shop", + "short-pillows-itch", + "silly-toys-sing", + "six-bottles-hear", + "sixty-clocks-design", + "slow-dolls-whisper", + "soft-planets-exercise", + "strong-jobs-eat", + "tame-melons-confess", + "ten-starfishes-attend", + "thick-dogs-provide", + "two-boxes-cheer", + "unlucky-spoons-trade" + ] +} diff --git a/.changeset/quick-dolphins-accept.md b/.changeset/quick-dolphins-accept.md new file mode 100644 index 0000000000..0e524a7210 --- /dev/null +++ b/.changeset/quick-dolphins-accept.md @@ -0,0 +1,6 @@ +--- +"@neo4j/graphql": patch +--- + +Deprecate attribute filtering using the non-generic version such as `title_EQ: "The Matrix"` in favor of the generic input `title: { eq: "The Matrix" }`. +The setting `excludeDeprecatedFields` now contains the option `attributeFilters` to remove these deprecated filters. diff --git a/.changeset/red-cows-shop.md b/.changeset/red-cows-shop.md new file mode 100644 index 0000000000..da7aaee903 --- /dev/null +++ b/.changeset/red-cows-shop.md @@ -0,0 +1,23 @@ +--- +"@neo4j/graphql": major +--- + +Implicit filtering fields have been removed, please use the explicit versions: + +```graphql +# Old syntax +{ + movies(where: { title: "The Matrix" }) { + title + } +} + +# New syntax +{ + movies(where: { title_EQ: "The Matrix" }) { + title + } +} +``` + +The `implicitEqualFilters` option of `excludeDeprecatedFields` has been removed. diff --git a/.changeset/short-pillows-itch.md b/.changeset/short-pillows-itch.md new file mode 100644 index 0000000000..d33cdecc13 --- /dev/null +++ b/.changeset/short-pillows-itch.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove support for `@unique` directive diff --git a/.changeset/silly-toys-sing.md b/.changeset/silly-toys-sing.md new file mode 100644 index 0000000000..8babb36fc9 --- /dev/null +++ b/.changeset/silly-toys-sing.md @@ -0,0 +1,13 @@ +--- +"@neo4j/graphql": patch +--- + +Add generic filters for aggregations: + +```graphql +{ + posts(where: { likesAggregate: { node: { rating: { average: { eq: 3.2 } } } } }) { + title + } +} +``` diff --git a/.changeset/six-bottles-hear.md b/.changeset/six-bottles-hear.md new file mode 100644 index 0000000000..2cdfea5b14 --- /dev/null +++ b/.changeset/six-bottles-hear.md @@ -0,0 +1,12 @@ +--- +"@neo4j/graphql": patch +--- + +Introduce the flag "aggregationFilters" to remove deprecated aggregation filters: + +```js +const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { excludeDeprecatedFields: { aggregationFilters: true } }, +}); +``` diff --git a/.changeset/sixty-clocks-design.md b/.changeset/sixty-clocks-design.md new file mode 100644 index 0000000000..af71152ae1 --- /dev/null +++ b/.changeset/sixty-clocks-design.md @@ -0,0 +1,5 @@ +--- +"@neo4j/introspector": patch +--- + +Changed how "@neo4j/introspector" generates list fields that now are generated as a list of non-nullable elements, as a list of nullable elements is not supported by Neo4j. diff --git a/.changeset/slow-dolls-whisper.md b/.changeset/slow-dolls-whisper.md new file mode 100644 index 0000000000..8e2b01f7b5 --- /dev/null +++ b/.changeset/slow-dolls-whisper.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +Remove `overwrite` field in connect operations diff --git a/.changeset/soft-planets-exercise.md b/.changeset/soft-planets-exercise.md new file mode 100644 index 0000000000..e7e22dfb8e --- /dev/null +++ b/.changeset/soft-planets-exercise.md @@ -0,0 +1,33 @@ +--- +"@neo4j/graphql": major +--- + +The deprecated `options` argument has been removed. + +Consider the following type definitions: + +```graphql +type Movie { + title: String! +} +``` + +The migration is as below: + +```graphql +# Old syntax +{ + movies(options: { first: 10, offset: 10, sort: [{ title: ASC }] }) { + title + } +} + +# New syntax +{ + movies(first: 10, offset: 10, sort: [{ title: ASC }]) { + title + } +} +``` + +The `deprecatedOptionsArgument` of `excludeDeprecatedFields` has been removed as it is now a no-op. diff --git a/.changeset/strong-jobs-eat.md b/.changeset/strong-jobs-eat.md new file mode 100644 index 0000000000..e79e7eed08 --- /dev/null +++ b/.changeset/strong-jobs-eat.md @@ -0,0 +1,27 @@ +--- +"@neo4j/graphql": major +--- + +Implicit set operations have been removed. For example: + +```graphql +# Old syntax +mutation { + updateMovies(where: { title_EQ: "Matrix" }, update: { title: "The Matrix" }) { + movies { + title + } + } +} + +# New syntax +mutation { + updateMovies(where: { title_EQ: "Matrix" }, update: { title_SET: "The Matrix" }) { + movies { + title + } + } +} +``` + +The `implicitSet` argument of `excludeDeprecatedFields` has been removed. diff --git a/.changeset/tame-melons-confess.md b/.changeset/tame-melons-confess.md new file mode 100644 index 0000000000..8a3c1e57d1 --- /dev/null +++ b/.changeset/tame-melons-confess.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Added a validation rule to avoid defining fields as lists of nullable elements, as Neo4j does not support this. \ No newline at end of file diff --git a/.changeset/ten-starfishes-attend.md b/.changeset/ten-starfishes-attend.md new file mode 100644 index 0000000000..660d78aab8 --- /dev/null +++ b/.changeset/ten-starfishes-attend.md @@ -0,0 +1,6 @@ +--- +"@neo4j/introspector": major +"@neo4j/graphql": major +--- + +The Neo4j GraphQL Library and Introspector now required Node.js 22 or greater. diff --git a/.changeset/thick-dogs-provide.md b/.changeset/thick-dogs-provide.md new file mode 100644 index 0000000000..94420920b6 --- /dev/null +++ b/.changeset/thick-dogs-provide.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +When performing a `connect` operation, new relationships are always created. diff --git a/.changeset/two-boxes-cheer.md b/.changeset/two-boxes-cheer.md new file mode 100644 index 0000000000..2ec8ab4ea7 --- /dev/null +++ b/.changeset/two-boxes-cheer.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": patch +--- + +Allow `app` to be overwritten in transaction metadata diff --git a/.changeset/unlucky-spoons-trade.md b/.changeset/unlucky-spoons-trade.md new file mode 100644 index 0000000000..764a93fc73 --- /dev/null +++ b/.changeset/unlucky-spoons-trade.md @@ -0,0 +1,25 @@ +--- +"@neo4j/graphql": minor +--- + +Introduce a new style for filtering relationships and connections. +The quantifiers `SOME` | `NONE` | `SINGLE` | `ALL` are now available as a nested input object. + +**Relationship** + +```graphql +{ + movies(where: { genres: { some: { name: { equals: "some genre" } } } }) { + actorCount + } +} +``` + +**Connection** +```graphql +{ + movies(where: { genresConnection: { some: { node: { name: { equals: "some genre" } } } } }) { + actorCount + } +} +``` diff --git a/packages/apollo-federation-subgraph-compatibility/package.json b/packages/apollo-federation-subgraph-compatibility/package.json index ce33720fb6..e82eae00cf 100644 --- a/packages/apollo-federation-subgraph-compatibility/package.json +++ b/packages/apollo-federation-subgraph-compatibility/package.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/server": "^4.7.0", "@graphql-tools/wrap": "^10.0.0", - "@neo4j/graphql": "^6.3.1", + "@neo4j/graphql": "^7.0.0-alpha.3", "graphql": "16.10.0", "graphql-tag": "^2.12.6", "neo4j-driver": "^5.8.0" diff --git a/packages/apollo-federation-subgraph-compatibility/src/type-defs.ts b/packages/apollo-federation-subgraph-compatibility/src/type-defs.ts index 18d42e97b6..155246f03f 100644 --- a/packages/apollo-federation-subgraph-compatibility/src/type-defs.ts +++ b/packages/apollo-federation-subgraph-compatibility/src/type-defs.ts @@ -42,39 +42,75 @@ export const typeDefs = gql` directive @custom on OBJECT - type Product @custom @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") { + type Product @node @custom @key(fields: "id") @key(fields: "sku package") @key(fields: "sku variation { id }") { id: ID! sku: String package: String - variation: ProductVariation @relationship(type: "HAS_VARIATION", direction: OUT) - dimensions: ProductDimension @relationship(type: "HAS_DIMENSIONS", direction: OUT) - createdBy: User @provides(fields: "totalProductsCreated") @relationship(type: "CREATED_BY", direction: OUT) + variation: ProductVariation + @cypher( + statement: """ + MATCH (this)-[:HAS_VARIATION]->(res:ProductVariation) + RETURN res + """ + columnName: "res" + ) + dimensions: ProductDimension + @cypher( + statement: """ + MATCH (this)-[:HAS_DIMENSIONS]->(res:ProductDimension) + RETURN res + """ + columnName: "res" + ) + createdBy: User + @provides(fields: "totalProductsCreated") + @cypher( + statement: """ + MATCH (this)-[:CREATED_BY]->(res:User) + RETURN res + """ + columnName: "res" + ) notes: String @tag(name: "internal") research: [ProductResearch!]! @relationship(type: "HAS_RESEARCH", direction: OUT) } - type DeprecatedProduct @key(fields: "sku package") { + type DeprecatedProduct @node @key(fields: "sku package") { sku: String! package: String! reason: String - createdBy: User @relationship(type: "CREATED_BY", direction: OUT) + createdBy: User + @cypher( + statement: """ + MATCH (this)-[:CREATED_BY]->(res:User) + RETURN res + """ + columnName: "res" + ) } - type ProductVariation { + type ProductVariation @node { id: ID! } - type ProductResearch @key(fields: "study { caseNumber }") { - study: CaseStudy! @relationship(type: "HAS_STUDY", direction: OUT) + type ProductResearch @key(fields: "study { caseNumber }") @node { + study: CaseStudy! + @cypher( + statement: """ + MATCH (this)-[:HAS_STUDY]->(res:CaseStudy) + RETURN res + """ + columnName: "res" + ) outcome: String } - type CaseStudy { + type CaseStudy @node { caseNumber: ID! description: String } - type ProductDimension @shareable { + type ProductDimension @shareable @node { size: String weight: Float unit: String @inaccessible @@ -93,7 +129,7 @@ export const typeDefs = gql` # Should be extend type as below # extend type User @key(fields: "email") { - type User @key(fields: "email") @extends { + type User @key(fields: "email") @node @extends { averageProductsCreatedPerYear: Int @requires(fields: "totalProductsCreated yearsOfEmployment") email: ID! @external name: String @override(from: "users") @@ -101,7 +137,7 @@ export const typeDefs = gql` yearsOfEmployment: Int! @external } - type Inventory @interfaceObject @key(fields: "id") { + type Inventory @node @interfaceObject @key(fields: "id") { id: ID! deprecatedProducts: [DeprecatedProduct!]! @relationship(type: "HAS_DEPRECATED_PRODUCT", direction: OUT) } diff --git a/packages/graphql/CHANGELOG.md b/packages/graphql/CHANGELOG.md index 9daefb6d26..0c8e1ea6cd 100644 --- a/packages/graphql/CHANGELOG.md +++ b/packages/graphql/CHANGELOG.md @@ -1,5 +1,385 @@ # @neo4j/graphql +## 7.0.0-alpha.3 + +### Major Changes + +- [#5997](https://github.com/neo4j/graphql/pull/5997) [`a716ef8`](https://github.com/neo4j/graphql/commit/a716ef8ec858aa8c6b51c285b3e2d899254c83fe) Thanks [@angrykoala](https://github.com/angrykoala)! - Remove `publish` method from `Neo4jGraphQLSubscriptionsEngine` interface as it is no longer used with CDC-based subscriptions. Implementing this method on custom engines will no longer have an effect, and it is no longer possible to call `publish` directly on `Neo4jGraphQLSubscriptionsCDCEngine` + +- [#5976](https://github.com/neo4j/graphql/pull/5976) [`7ddde75`](https://github.com/neo4j/graphql/commit/7ddde75d9828c737e3849c49b6b91f4b2b9b8044) Thanks [@angrykoala](https://github.com/angrykoala)! - Sets addVersionPrefix to true by default, this will prepend the Cypher version to all queries by default, ensuring that the correct Cypher version is used in Neo4j: + + ```cypher + CYPHER 5 + MATCH(this:Movie) + ``` + + This may be incompatible with older versions of Neo4j and can be disabled by setting `cypherQueryOption.addVersionPrefix` in the context to false: + + ```js + { + cypherQueryOptions: { + addVersionPrefix: true, + }, + } + ``` + + For example, for an apollo server: + + ```js + await startStandaloneServer(server, { + context: async ({ req }) => ({ + req, + cypherQueryOptions: { + addVersionPrefix: false, + }, + }), + listen: { port: 4000 }, + }); + ``` + +### Patch Changes + +- [#6007](https://github.com/neo4j/graphql/pull/6007) [`48aec51`](https://github.com/neo4j/graphql/commit/48aec512b4707d9b9aa74f05d382eb6980e08971) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Allow `app` to be overwritten in transaction metadata + +## 7.0.0-alpha.2 + +### Major Changes + +- [#5936](https://github.com/neo4j/graphql/pull/5936) [`d48ea32`](https://github.com/neo4j/graphql/commit/d48ea327db022774c73a8adfada1a8d498590c2d) Thanks [@mjfwebb](https://github.com/mjfwebb)! - Changes the result projection where there are multiple relationships between two nodes. + + In the case of using the connection API then multiple relationships will still be represented, as there is the ability to select the relationship properties. In the non-connection API case, the duplicate results will only return distinct results. + +- [#5931](https://github.com/neo4j/graphql/pull/5931) [`5ce7d1d`](https://github.com/neo4j/graphql/commit/5ce7d1dff5287aa9d24beaf3992f1f66c7b62d94) Thanks [@darrellwarde](https://github.com/darrellwarde)! - `DateTime` and `Time` values are now converted from strings into temporal types in the generated Cypher instead of in server code using the driver. This could result in different values when the database is in a different timezone to the GraphQL server. + +- [#5933](https://github.com/neo4j/graphql/pull/5933) [`8bdcf6b`](https://github.com/neo4j/graphql/commit/8bdcf6b36fba1442f75fe8401cf170ed17339cdb) Thanks [@mjfwebb](https://github.com/mjfwebb)! - When performing a `connect` operation, new relationships are always created. + +## 7.0.0-alpha.1 + +### Major Changes + +- [#5927](https://github.com/neo4j/graphql/pull/5927) [`530d8cd`](https://github.com/neo4j/graphql/commit/530d8cddf91f031b849bbab6a668277b2c5986bf) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - The `typename_IN` filter has been renamed to `typename`. + +- [#5898](https://github.com/neo4j/graphql/pull/5898) [`a912404`](https://github.com/neo4j/graphql/commit/a91240457f8ccf51d3f6b11ecedf1101678306cd) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Aggregations are no longer generated for `ID` fields. + +### Minor Changes + +- [#5868](https://github.com/neo4j/graphql/pull/5868) [`46ab2fa`](https://github.com/neo4j/graphql/commit/46ab2fa03b31610a26fa559189fd3af22ab7438e) Thanks [@angrykoala](https://github.com/angrykoala)! - Add suport for generic update operators: + + ```graphql + mutation { + updateMovies(update: { name: { set: "The Matrix" } }) { + movies { + id + name + } + } + } + ``` + +- [#5873](https://github.com/neo4j/graphql/pull/5873) [`17911fc`](https://github.com/neo4j/graphql/commit/17911fc197105f5fafc06ce851669af6fc07b18a) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Introduce a new style for filtering relationships and connections. + The quantifiers `SOME` | `NONE` | `SINGLE` | `ALL` are now available as a nested input object. + + **Relationship** + + ```graphql + { + movies(where: { genres: { some: { name: { equals: "some genre" } } } }) { + actorCount + } + } + ``` + + **Connection** + + ```graphql + { + movies(where: { genresConnection: { some: { node: { name: { equals: "some genre" } } } } }) { + actorCount + } + } + ``` + +### Patch Changes + +- [#5871](https://github.com/neo4j/graphql/pull/5871) [`722c650`](https://github.com/neo4j/graphql/commit/722c6507977072122041e985b94a84a707179f39) Thanks [@angrykoala](https://github.com/angrykoala)! - Deprecate individual mutations in favor of generic mutations + + - `_SET` + - `_POP` + - `_PUSH` + - `_INCREMENT` + - `_ADD` + - `_DECREMENT` + - `_SUBTRACT` + - `_MULTIPLY` + - `_DIVIDE` + +- [#5882](https://github.com/neo4j/graphql/pull/5882) [`7254acf`](https://github.com/neo4j/graphql/commit/7254acf1b7bb83a35cea580143a6012355bc02d8) Thanks [@angrykoala](https://github.com/angrykoala)! - Deprecates old aggregation filters for relationships in favor of more generic filters: + + Before: + + ```js + query Movies { + movies( + where: { actorsAggregate: { node: { lastRating_AVERAGE_GT: 6 } } } + ) { + title + } + } + ``` + + Now: + + ```js + query Movies { + movies( + where: { + actorsAggregate: { node: { lastRating: { average: { gt: 6 } } } } + } + ) { + title + } + } + ``` + +- [#5897](https://github.com/neo4j/graphql/pull/5897) [`4f3b068`](https://github.com/neo4j/graphql/commit/4f3b068cfe4123109bb8a27bacef775fb897a87e) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Deprecate relationship filtering using the non-generic version such as `actors_SOME: { title_EQ: "The Matrix" }` in favor of the generic input `actors: { some: { title: { eq: "The Matrix" } } }`. + The setting `excludeDeprecatedFields` now contains the option `relationshipFilters` to remove these deprecated filters. + +- [#5897](https://github.com/neo4j/graphql/pull/5897) [`917482b`](https://github.com/neo4j/graphql/commit/917482b675ec3de7dc06ca110e2fccf93024115f) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Deprecate attribute filtering using the non-generic version such as `title_EQ: "The Matrix"` in favor of the generic input `title: { eq: "The Matrix" }`. + The setting `excludeDeprecatedFields` now contains the option `attributeFilters` to remove these deprecated filters. + +- [#5879](https://github.com/neo4j/graphql/pull/5879) [`5c7ba22`](https://github.com/neo4j/graphql/commit/5c7ba22afc8fc0df86a148f31ce61691586f8cf3) Thanks [@angrykoala](https://github.com/angrykoala)! - Add generic filters for aggregations: + + ```graphql + { + posts(where: { likesAggregate: { node: { rating: { average: { eq: 3.2 } } } } }) { + title + } + } + ``` + +- [#5882](https://github.com/neo4j/graphql/pull/5882) [`7254acf`](https://github.com/neo4j/graphql/commit/7254acf1b7bb83a35cea580143a6012355bc02d8) Thanks [@angrykoala](https://github.com/angrykoala)! - Introduce the flag "aggregationFilters" to remove deprecated aggregation filters: + + ```js + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { excludeDeprecatedFields: { aggregationFilters: true } }, + }); + ``` + +## 7.0.0-alpha.0 + +### Major Changes + +- [#5899](https://github.com/neo4j/graphql/pull/5899) [`7335d8f`](https://github.com/neo4j/graphql/commit/7335d8f416bbfa08feab0fe4983f89590f984e1c) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Nested mutation operations now follow the relationship direction behaviour as defined in `queryDirection` + +- [#5872](https://github.com/neo4j/graphql/pull/5872) [`925ad8d`](https://github.com/neo4j/graphql/commit/925ad8dedc307200d1c3fd813e531325940d8f8f) Thanks [@angrykoala](https://github.com/angrykoala)! - Remove `@private` directive. This directive was intended to be used with the library `@neo4j/graphql-ogm` which is no longer supported. + +- [#5895](https://github.com/neo4j/graphql/pull/5895) [`6afcadd`](https://github.com/neo4j/graphql/commit/6afcaddbfc62549c6c610a2199513bf4c719486c) Thanks [@angrykoala](https://github.com/angrykoala)! - Fails schema generation if there are conflicting plural names in types. For example, the following schema will fail, due to ambiguous `Techs` plural + + ```graphql + type Tech @node(plural: "Techs") { + name: String + } + + type Techs { + value: String + } + ``` + +- [#5755](https://github.com/neo4j/graphql/pull/5755) [`9c75f92`](https://github.com/neo4j/graphql/commit/9c75f925884de42f64e1b5c3086cc87c114727bd) Thanks [@angrykoala](https://github.com/angrykoala)! - Remove support for `connectOrCreate` operations + +- [#5778](https://github.com/neo4j/graphql/pull/5778) [`56022ba`](https://github.com/neo4j/graphql/commit/56022ba38d8beb6cb5d7bbfb5e856fd57d9660c5) Thanks [@darrellwarde](https://github.com/darrellwarde)! - The deprecated `directed` argument has been removed, and `queryDirection` now only accepts two possible values - `DIRECTED` (default) and `UNDIRECTED`. + + Additionally, the `directedArgument` setting of `excludeDeprecatedFields` has been removed as these deprecated fields have been removed. + +- [#5819](https://github.com/neo4j/graphql/pull/5819) [`ac1fa62`](https://github.com/neo4j/graphql/commit/ac1fa629f1eb8b248116bd9dedaabc02117fdbee) Thanks [@angrykoala](https://github.com/angrykoala)! - Single element relationships have been removed in favor of list relationships: + + Before + + ```graphql + type Movie { + director: Person @relationship(type: "DIRECTED", direction: "IN") + } + ``` + + After + + ```graphql + type Movie { + director: [Person!]! @relationship(type: "DIRECTED", direction: "IN") + } + ``` + + This requires updating filters, clients and auth rules to use the list filter operations. + + Single element relationships cannot be reliably enforced, leading to a data inconsistent with the schema. If the GraphQL model requires 1-1 relationships (such as in federations) these can now be achieved with the `@cypher` directive instead: + + ```graphql + type Movie { + director: Person + @cypher( + statement: """ + MATCH(this)-[:ACTED_IN]->(p:Person) + RETURN p + """ + columnName: "p" + ) + } + ``` + +- [#5762](https://github.com/neo4j/graphql/pull/5762) [`87e416b`](https://github.com/neo4j/graphql/commit/87e416b2547b75824d9782fd5da90c003437e7c0) Thanks [@darrellwarde](https://github.com/darrellwarde)! - There have been major changes to the way that full-text search operates. + + The directive now requires the specification of an index name, query name, and indexed fields. + + ```graphql + input FulltextInput { + indexName: String! + queryName: String! + fields: [String]! + } + + """ + Informs @neo4j/graphql that there should be a fulltext index in the database, allows users to search by the index in the generated schema. + """ + directive @fulltext(indexes: [FulltextInput]!) on OBJECT + ``` + + Here is an example of how this might be used: + + ```graphql + type Movie @node @fulltext(indexName: "movieTitleIndex", queryName: "moviesByTitle", fields: ["title"]) { + title: String! + } + ``` + + Full-text search was previously available in two different locations. + + The following form has now been completely removed: + + ```graphql + # Removed + { + movies(fulltext: { movieTitleIndex: { phrase: "The Matrix" } }) { + title + } + } + ``` + + The following form as a root-level query has been changed: + + ```graphql + # Old query + query { + moviesByTitle(phrase: "The Matrix") { + score + movies { + title + } + } + } + + # New query + query { + moviesByTitle(phrase: "The Matrix") { + edges { + score + node { + title + } + } + } + } + ``` + + The new form is as a Relay connection, which allows for pagination using cursors and access to the `pageInfo` field. + +- [#5820](https://github.com/neo4j/graphql/pull/5820) [`d8d59f8`](https://github.com/neo4j/graphql/commit/d8d59f80480017d27b49b062321a9a15b6494a96) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Change the way how `@node` behaves, `@node` is now required, and GraphQL Object types without the directive `@node` will no longer considered as a Neo4j Nodes representation. + Queries and Mutations will be generated only for types with the `@node` directive. + +- [#5801](https://github.com/neo4j/graphql/pull/5801) [`95ce8bb`](https://github.com/neo4j/graphql/commit/95ce8bb884bddaf20d751f2448b5504a7b94d081) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Implicit filtering fields have been removed, please use the explicit versions: + + ```graphql + # Old syntax + { + movies(where: { title: "The Matrix" }) { + title + } + } + + # New syntax + { + movies(where: { title_EQ: "The Matrix" }) { + title + } + } + ``` + + The `implicitEqualFilters` option of `excludeDeprecatedFields` has been removed. + +- [#5755](https://github.com/neo4j/graphql/pull/5755) [`9c75f92`](https://github.com/neo4j/graphql/commit/9c75f925884de42f64e1b5c3086cc87c114727bd) Thanks [@angrykoala](https://github.com/angrykoala)! - Remove support for `@unique` directive + +- [#5768](https://github.com/neo4j/graphql/pull/5768) [`e338590`](https://github.com/neo4j/graphql/commit/e338590d25216cced8252cfe3d0789d97952c20d) Thanks [@angrykoala](https://github.com/angrykoala)! - Remove `overwrite` field in connect operations + +- [#5777](https://github.com/neo4j/graphql/pull/5777) [`0ecfd71`](https://github.com/neo4j/graphql/commit/0ecfd71a1431c5f98fde30319eefd5b018a06701) Thanks [@darrellwarde](https://github.com/darrellwarde)! - The deprecated `options` argument has been removed. + + Consider the following type definitions: + + ```graphql + type Movie { + title: String! + } + ``` + + The migration is as below: + + ```graphql + # Old syntax + { + movies(options: { first: 10, offset: 10, sort: [{ title: ASC }] }) { + title + } + } + + # New syntax + { + movies(first: 10, offset: 10, sort: [{ title: ASC }]) { + title + } + } + ``` + + The `deprecatedOptionsArgument` of `excludeDeprecatedFields` has been removed as it is now a no-op. + +- [#5802](https://github.com/neo4j/graphql/pull/5802) [`99cb9aa`](https://github.com/neo4j/graphql/commit/99cb9aa866eed04224d790bfccab9c3d3add78b7) Thanks [@darrellwarde](https://github.com/darrellwarde)! - Implicit set operations have been removed. For example: + + ```graphql + # Old syntax + mutation { + updateMovies(where: { title_EQ: "Matrix" }, update: { title: "The Matrix" }) { + movies { + title + } + } + } + + # New syntax + mutation { + updateMovies(where: { title_EQ: "Matrix" }, update: { title_SET: "The Matrix" }) { + movies { + title + } + } + } + ``` + + The `implicitSet` argument of `excludeDeprecatedFields` has been removed. + +- [#5789](https://github.com/neo4j/graphql/pull/5789) [`1a07d40`](https://github.com/neo4j/graphql/commit/1a07d40888e89c5cd9a40edc16f1742e27bff687) Thanks [@darrellwarde](https://github.com/darrellwarde)! - The Neo4j GraphQL Library and Introspector now required Node.js 22 or greater. + +### Patch Changes + +- [#5837](https://github.com/neo4j/graphql/pull/5837) [`721691a`](https://github.com/neo4j/graphql/commit/721691a84eaa34996c0c97edb7ede1ae4775dd2f) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Added a validation rule to avoid defining fields as lists of nullable elements, as Neo4j does not support this. + ## 6.3.1 ### Patch Changes diff --git a/packages/graphql/package.json b/packages/graphql/package.json index b0a63520fe..34ad8d7e47 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@neo4j/graphql", - "version": "6.3.1", + "version": "7.0.0-alpha.3", "description": "A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations", "keywords": [ "neo4j", @@ -22,7 +22,7 @@ "dist/**/*.js.map" ], "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "scripts": { "build": "tsc --build src/tsconfig.production.json", @@ -85,6 +85,7 @@ "dot-prop": "^6.0.1", "graphql-compose": "^9.0.8", "graphql-parse-resolve-info": "^4.12.3", + "graphql-query-complexity": "^1.0.0", "graphql-relay": "^0.10.0", "jose": "^5.0.0", "pluralize": "^8.0.0", diff --git a/packages/graphql/src/classes/ComplexityEstimatorHelper.ts b/packages/graphql/src/classes/ComplexityEstimatorHelper.ts new file mode 100644 index 0000000000..595420c74c --- /dev/null +++ b/packages/graphql/src/classes/ComplexityEstimatorHelper.ts @@ -0,0 +1,106 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { type DefinitionNode,type GraphQLFieldExtensions, type GraphQLSchema, GraphQLInterfaceType, GraphQLObjectType, Kind } from "graphql"; +import type { ComplexityEstimator} from "graphql-query-complexity"; +import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity"; + +export class ComplexityEstimatorHelper { + private objectTypeNameToFieldNamesMapForComplexityExtensions: Map; + private useComplexityEstimators: boolean; + + constructor(useComplexityEstimators: boolean) { + this.useComplexityEstimators = useComplexityEstimators + this.objectTypeNameToFieldNamesMapForComplexityExtensions = new Map(); + } + + public registerField(parentObjectTypeNameOrInterfaceTypeName: string, fieldName: string): void { + if(this.useComplexityEstimators) { + const existingFieldsForTypeName = this.objectTypeNameToFieldNamesMapForComplexityExtensions.get(parentObjectTypeNameOrInterfaceTypeName) ?? [] + this.objectTypeNameToFieldNamesMapForComplexityExtensions.set(parentObjectTypeNameOrInterfaceTypeName, existingFieldsForTypeName.concat(fieldName)) + } + } + + public hydrateDefinitionNodeWithComplexityExtensions(definition: DefinitionNode): DefinitionNode { + if(definition.kind !== Kind.OBJECT_TYPE_DEFINITION && definition.kind !== Kind.INTERFACE_TYPE_DEFINITION) { + return definition; + } + if(!this.objectTypeNameToFieldNamesMapForComplexityExtensions.has(definition.name.value)) { + return definition + } + + const fieldsWithComplexity = definition.fields?.map(f => { + const hasFieldComplexityEstimator = this.getFieldsForParentTypeName(definition.name.value).find(fieldName => fieldName === f.name.value) + if (!hasFieldComplexityEstimator) { + return f + } + return { + ...f, + extensions: { + // COMPLEXITY FORMULA + // c = c_child + lvl_limit * c_field, where + // c_field = 1 + // lvl_limit defaults to 1 + // c_child comes from simpleEstimator + complexity: ({childComplexity, args}) => { + const fieldDefaultComplexity = 1 + const defaultLimitIfNotProvided = 1 + if(args.limit ?? args.first) { + return childComplexity + (args.limit ?? args.first) * fieldDefaultComplexity + } + return childComplexity + defaultLimitIfNotProvided + + }, + }, + } + }) + return { + ...definition, + fields: fieldsWithComplexity, + } + } + + + public hydrateSchemaFromSDLWithASTNodeExtensions(schema: GraphQLSchema): void { + const types = schema.getTypeMap(); + Object.values(types).forEach((type) => { + if (type instanceof GraphQLObjectType || type instanceof GraphQLInterfaceType) { + const fields = type.getFields(); + Object.values(fields).forEach((field) => { + if (field.astNode && 'extensions' in field.astNode) { + field.extensions = field.astNode.extensions as GraphQLFieldExtensions; + } + }); + } + }); + } + + public getComplexityEstimators(): ComplexityEstimator[] { + if (!this.useComplexityEstimators) { + return [] + } + return [ + fieldExtensionsEstimator(), + simpleEstimator({ defaultComplexity: 1 }), + ]; + } + + private getFieldsForParentTypeName(parentObjectTypeNameOrInterfaceTypeName: string): string[] { + return this.objectTypeNameToFieldNamesMapForComplexityExtensions.get(parentObjectTypeNameOrInterfaceTypeName) || [] + } +} diff --git a/packages/graphql/src/classes/Executor.ts b/packages/graphql/src/classes/Executor.ts index 13f51728b4..774c3c534f 100644 --- a/packages/graphql/src/classes/Executor.ts +++ b/packages/graphql/src/classes/Executor.ts @@ -184,7 +184,8 @@ export class Executor { } private getCypherVersionStatement(): string { - if (this.cypherQueryOptions?.addVersionPrefix) { + const addVersionPrefixDefault=true + if (this.cypherQueryOptions?.addVersionPrefix ?? addVersionPrefixDefault) { return `CYPHER ${SUPPORTED_CYPHER_VERSION}\n`; } return ""; @@ -208,8 +209,8 @@ export class Executor { private getTransactionConfig(info?: GraphQLResolveInfo): TransactionConfig { const transactionConfig: TransactionConfig = { metadata: { - ...this.transactionMetadata, app: APP_ID, + ...this.transactionMetadata, type: "user-transpiled", }, }; diff --git a/packages/graphql/src/classes/Neo4jGraphQL.test.ts b/packages/graphql/src/classes/Neo4jGraphQL.test.ts index f3c49f9ea8..608926f2f8 100644 --- a/packages/graphql/src/classes/Neo4jGraphQL.test.ts +++ b/packages/graphql/src/classes/Neo4jGraphQL.test.ts @@ -39,10 +39,14 @@ describe("Neo4jGraphQL", () => { describe("getExecutableSchema", () => { test("error should contain path", async () => { let schema: GraphQLSchema | undefined = undefined; + const typeDefs = /* GraphQL */ ` + type User @node @authorization(filter: [{ where: { banana: { id: "$jwt.sub" } } }]) { + id: ID + } + `; const errors: Error[] = await getErrorAsync(async () => { schema = await new Neo4jGraphQL({ - typeDefs: - 'type User @node @authorization(filter: [{ where: { banana: { id: "$jwt.sub" } } }]) {id: ID}', + typeDefs, }).getExecutableSchema(); }); expect(errors).toHaveLength(1); diff --git a/packages/graphql/src/classes/Neo4jGraphQL.ts b/packages/graphql/src/classes/Neo4jGraphQL.ts index 83eddd4782..e4831ec70c 100644 --- a/packages/graphql/src/classes/Neo4jGraphQL.ts +++ b/packages/graphql/src/classes/Neo4jGraphQL.ts @@ -23,13 +23,13 @@ import type { IExecutableSchemaDefinition } from "@graphql-tools/schema"; import { addResolversToSchema, makeExecutableSchema } from "@graphql-tools/schema"; import { forEachField, getResolversFromSchema } from "@graphql-tools/utils"; import Debug from "debug"; -import type { DocumentNode, GraphQLSchema } from "graphql"; +import { type DocumentNode, type GraphQLSchema } from "graphql"; import type { Driver, SessionConfig } from "neo4j-driver"; import { DEBUG_ALL } from "../constants"; import { makeAugmentedSchema } from "../schema"; import type { Neo4jGraphQLSchemaModel } from "../schema-model/Neo4jGraphQLSchemaModel"; import { generateModel } from "../schema-model/generate-model"; -import { getDefinitionNodes } from "../schema/get-definition-nodes"; +import { getDefinitionCollection } from "../schema-model/parser/definition-collection"; import { makeDocumentToAugment } from "../schema/make-document-to-augment"; import type { WrapResolverArguments } from "../schema/resolvers/composition/wrap-query-and-mutation"; import { wrapQueryAndMutation } from "../schema/resolvers/composition/wrap-query-and-mutation"; @@ -50,6 +50,7 @@ import { Neo4jGraphQLSubscriptionsCDCEngine } from "./subscription/Neo4jGraphQLS import { assertIndexesAndConstraints } from "./utils/asserts-indexes-and-constraints"; import { generateResolverComposition } from "./utils/generate-resolvers-composition"; import checkNeo4jCompat from "./utils/verify-database"; +import { ComplexityEstimatorHelper } from "./ComplexityEstimatorHelper"; type TypeDefinitions = string | DocumentNode | TypeDefinitions[] | (() => TypeDefinitions); @@ -75,6 +76,7 @@ class Neo4jGraphQL { private jwtFieldsMap?: Map; private schemaModel?: Neo4jGraphQLSchemaModel; + private complexityEstimatorHelper: ComplexityEstimatorHelper; private executableSchema?: Promise; private subgraphSchema?: Promise; @@ -108,6 +110,8 @@ class Neo4jGraphQL { this.authorization = new Neo4jGraphQLAuthorization(authorizationSettings); } + + this.complexityEstimatorHelper = new ComplexityEstimatorHelper(!!this.features.complexityEstimators); } public async getSchema(): Promise { @@ -365,12 +369,17 @@ class Neo4jGraphQL { interfaceTypes: interfaces, unionTypes: unions, objectTypes: objects, - } = getDefinitionNodes(initialDocument); + } = getDefinitionCollection(initialDocument); validateDocument({ document: initialDocument, features: this.features, - additionalDefinitions: { enums, interfaces, unions, objects }, + additionalDefinitions: { + enums: [...enums.values()], + interfaces: [...interfaces.values()], + unions: [...unions.values()], + objects: [...objects.values()], + }, userCustomResolvers: this.resolvers, }); } @@ -388,10 +397,16 @@ class Neo4jGraphQL { features: this.features, userCustomResolvers: this.resolvers, schemaModel: this.schemaModel, + complexityEstimatorHelper: this.complexityEstimatorHelper, }); if (this.validate) { - validateUserDefinition({ userDocument: document, augmentedDocument: typeDefs, jwt: jwt?.type }); + validateUserDefinition({ + userDocument: document, + augmentedDocument: typeDefs, + jwt: jwt?.type, + features: this.features, + }); } this._nodes = nodes; @@ -401,6 +416,7 @@ class Neo4jGraphQL { typeDefs, resolvers, }); + this.complexityEstimatorHelper.hydrateSchemaFromSDLWithASTNodeExtensions(schema); resolve(this.composeSchema(schema)); }); @@ -421,7 +437,7 @@ class Neo4jGraphQL { interfaceTypes: interfaces, unionTypes: unions, objectTypes: objects, - } = getDefinitionNodes(initialDocument); + } = getDefinitionCollection(initialDocument); validateDocument({ document: initialDocument, @@ -429,10 +445,10 @@ class Neo4jGraphQL { additionalDefinitions: { additionalDirectives: directives, additionalTypes: types, - enums, - interfaces, - unions, - objects, + enums: [...enums.values()], + interfaces: [...interfaces.values()], + unions: [...unions.values()], + objects: [...objects.values()], }, userCustomResolvers: this.resolvers, }); @@ -452,6 +468,7 @@ class Neo4jGraphQL { userCustomResolvers: this.resolvers, subgraph, schemaModel: this.schemaModel, + complexityEstimatorHelper: this.complexityEstimatorHelper, }); if (this.validate) { @@ -461,6 +478,7 @@ class Neo4jGraphQL { additionalDirectives: directives, additionalTypes: types, jwt: jwt?.type, + features: this.features, }); } diff --git a/packages/graphql/src/classes/Node.ts b/packages/graphql/src/classes/Node.ts index a2c2b85650..2e4e95c291 100644 --- a/packages/graphql/src/classes/Node.ts +++ b/packages/graphql/src/classes/Node.ts @@ -78,8 +78,6 @@ export type MutableField = | TemporalField | CypherField; -type ConstrainableField = PrimitiveField | CustomScalarField | CustomEnumField | TemporalField; - export type RootTypeFieldNames = { create: string; read: string; @@ -122,7 +120,6 @@ class Node extends GraphElement { public interfaces: NamedTypeNode[]; public objectFields: ObjectField[]; public nodeDirective?: NodeDirective; - public fulltextDirective?: FullText; public description?: string; public limit?: LimitDirective; public singular: string; @@ -143,7 +140,6 @@ class Node extends GraphElement { this.interfaces = input.interfaces; this.objectFields = input.objectFields; this.nodeDirective = input.nodeDirective; - this.fulltextDirective = input.fulltextDirective; this.limit = input.limitDirective; this.isGlobalNode = input.isGlobalNode; this._idField = input.globalIdField; @@ -167,20 +163,21 @@ class Node extends GraphElement { ]; } - public get constrainableFields(): ConstrainableField[] { + /** Fields you can apply auth allow and bind to */ + // Maybe we can remove this as they may not be used anymore in the new auth system + public get authableFields(): MutableField[] { return [ ...this.primitiveFields, ...this.scalarFields, ...this.enumFields, + ...this.unionFields, + ...this.objectFields, ...this.temporalFields, ...this.pointFields, + ...this.cypherFields, ]; } - public get uniqueFields(): ConstrainableField[] { - return this.constrainableFields.filter((field) => field.unique); - } - private get pascalCaseSingular(): string { return upperFirst(this.singular); } diff --git a/packages/graphql/src/classes/index.ts b/packages/graphql/src/classes/index.ts index 64da04bf09..e1f4ee462e 100644 --- a/packages/graphql/src/classes/index.ts +++ b/packages/graphql/src/classes/index.ts @@ -17,9 +17,12 @@ * limitations under the License. */ +import { fieldExtensionsEstimator, simpleEstimator } from "graphql-query-complexity"; + export * from "./Error"; export { GraphElement } from "./GraphElement"; export { Neo4jDatabaseInfo } from "./Neo4jDatabaseInfo"; export { default as Neo4jGraphQL, Neo4jGraphQLConstructor } from "./Neo4jGraphQL"; export { default as Node, NodeConstructor } from "./Node"; export { default as Relationship } from "./Relationship"; +export const DefaultComplexityEstimators = [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })]; diff --git a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsCDCEngine.ts b/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsCDCEngine.ts index 3091029824..d042ac744a 100644 --- a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsCDCEngine.ts +++ b/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsCDCEngine.ts @@ -23,7 +23,7 @@ import type { Driver, QueryConfig } from "neo4j-driver"; import { Memoize } from "typescript-memoize"; import { APP_ID } from "../../constants"; import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel"; -import type { Neo4jGraphQLSubscriptionsEngine, SubscriptionEngineContext, SubscriptionsEvent } from "../../types"; +import type { Neo4jGraphQLSubscriptionsEngine, SubscriptionEngineContext } from "../../types"; import { CDCApi } from "./cdc/cdc-api"; import { CDCEventParser } from "./cdc/cdc-event-parser"; @@ -65,10 +65,6 @@ export class Neo4jGraphQLSubscriptionsCDCEngine implements Neo4jGraphQLSubscript return this._parser; } - public publish(_eventMeta: SubscriptionsEvent): void | Promise { - // Disable Default Publishing mechanism - } - public async init({ schemaModel }: SubscriptionEngineContext): Promise { await this.cdcApi.updateCursor(); this._parser = new CDCEventParser(schemaModel); diff --git a/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts b/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts index a5c3bf2d6e..8bc18d8b5d 100644 --- a/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts +++ b/packages/graphql/src/classes/utils/asserts-indexes-and-constraints.ts @@ -22,7 +22,6 @@ import type { Driver, Session } from "neo4j-driver"; import { DEBUG_EXECUTE } from "../../constants"; import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel"; import type { ConcreteEntity } from "../../schema-model/entity/ConcreteEntity"; -import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { Neo4jGraphQLSessionConfig } from "../Executor"; const debug = Debug(DEBUG_EXECUTE); @@ -134,7 +133,7 @@ async function checkIndexesAndConstraints({ schemaModel: Neo4jGraphQLSchemaModel; session: Session; }): Promise { - const missingConstraints = await getMissingConstraints({ schemaModel, session }); + const missingConstraints = await getMissingConstraints({ session }); if (missingConstraints.length) { const missingConstraintMessages = missingConstraints.map( @@ -151,10 +150,7 @@ async function checkIndexesAndConstraints({ for (const entity of schemaModel.concreteEntities) { if (entity.annotations.fulltext) { entity.annotations.fulltext.indexes.forEach((index) => { - const indexName = index.indexName || index.name; // TODO remove indexName assignment and undefined check once the name argument has been removed. - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } + const indexName = index.indexName; const existingIndex = existingIndexes[indexName]; if (!existingIndex) { @@ -197,13 +193,7 @@ async function checkIndexesAndConstraints({ type MissingConstraint = { constraintName: string; label: string; property: string }; -async function getMissingConstraints({ - schemaModel, - session, -}: { - schemaModel: Neo4jGraphQLSchemaModel; - session: Session; -}): Promise { +async function getMissingConstraints({ session }: { session: Session }): Promise { const existingConstraints: Record = {}; const constraintsCypher = "SHOW UNIQUE CONSTRAINTS"; @@ -229,36 +219,5 @@ async function getMissingConstraints({ const missingConstraints: MissingConstraint[] = []; - for (const entity of schemaModel.concreteEntities) { - const entityAdapter = new ConcreteEntityAdapter(entity); - for (const uniqueField of entityAdapter.uniqueFields) { - if (!uniqueField.annotations.unique) { - continue; - } - - let anyLabelHasConstraint = false; - for (const label of entity.labels) { - // If any of the constraints for the label already exist, skip to the next unique field - if (existingConstraints[label]?.includes(uniqueField.databaseName)) { - anyLabelHasConstraint = true; - break; - } - } - - if (anyLabelHasConstraint === false) { - // TODO: The fallback value of `${entity.name}_${uniqueField.databaseName}` should be changed to use the main label of the entity - // But this can only be done once the translation layer has been updated to use the schema model instead of the Node class - const constraintName = - uniqueField.annotations.unique.constraintName || `${entity.name}_${uniqueField.databaseName}`; - - missingConstraints.push({ - constraintName, - label: entityAdapter.getMainLabel(), - property: uniqueField.databaseName, - }); - } - } - } - return missingConstraints; } diff --git a/packages/graphql/src/constants.ts b/packages/graphql/src/constants.ts index 98475f18bc..3302d9b789 100644 --- a/packages/graphql/src/constants.ts +++ b/packages/graphql/src/constants.ts @@ -75,10 +75,6 @@ export const LOGICAL_OPERATORS = ["AND", "OR", "NOT"] as const; export const AGGREGATION_COMPARISON_OPERATORS = ["EQUAL", "GT", "GTE", "LT", "LTE"] as const; export enum RelationshipQueryDirectionOption { - DEFAULT_DIRECTED = "DEFAULT_DIRECTED", - DEFAULT_UNDIRECTED = "DEFAULT_UNDIRECTED", - DIRECTED_ONLY = "DIRECTED_ONLY", - UNDIRECTED_ONLY = "UNDIRECTED_ONLY", DIRECTED = "DIRECTED", UNDIRECTED = "UNDIRECTED", } diff --git a/packages/graphql/src/graphql/directives/arguments/enums/RelationshipQueryDirection.ts b/packages/graphql/src/graphql/directives/arguments/enums/RelationshipQueryDirection.ts index 4313a24870..b4c576b731 100644 --- a/packages/graphql/src/graphql/directives/arguments/enums/RelationshipQueryDirection.ts +++ b/packages/graphql/src/graphql/directives/arguments/enums/RelationshipQueryDirection.ts @@ -23,20 +23,6 @@ import { RelationshipQueryDirectionOption } from "../../../../constants"; export const RelationshipQueryDirectionEnum = new GraphQLEnumType({ name: "RelationshipQueryDirection", values: { - [RelationshipQueryDirectionOption.DEFAULT_DIRECTED]: { - deprecationReason: - "DEFAULT_DIRECTED is deprecated without alternative and it will be removed in future versions, this is following the deprecation of the generated `directed` argument", - }, - [RelationshipQueryDirectionOption.DEFAULT_UNDIRECTED]: { - deprecationReason: - "DEFAULT_UNDIRECTED is deprecated without alternative and it will be removed in future versions, this is following the deprecation of the generated `directed` argument", - }, - [RelationshipQueryDirectionOption.DIRECTED_ONLY]: { - deprecationReason: "DIRECTED_ONLY is deprecated, please use DIRECTED.", - }, - [RelationshipQueryDirectionOption.UNDIRECTED_ONLY]: { - deprecationReason: "UNDIRECTED_ONLY is deprecated, please use UNDIRECTED.", - }, [RelationshipQueryDirectionOption.DIRECTED]: {}, [RelationshipQueryDirectionOption.UNDIRECTED]: {}, }, diff --git a/packages/graphql/src/graphql/directives/fulltext.ts b/packages/graphql/src/graphql/directives/fulltext.ts index be6959f02f..81178e93bd 100644 --- a/packages/graphql/src/graphql/directives/fulltext.ts +++ b/packages/graphql/src/graphql/directives/fulltext.ts @@ -26,11 +26,6 @@ import { GraphQLString, } from "graphql"; -const deprecationReason = - "The name argument has been deprecated and will be removed in future versions " + - "Please use indexName instead. More information about the changes to @fulltext can be found at " + - "https://neo4j.com/docs/graphql-manual/current/guides/v4-migration/#_fulltext_changes."; - export const fulltextDirective = new GraphQLDirective({ name: "fulltext", description: @@ -40,12 +35,8 @@ export const fulltextDirective = new GraphQLDirective({ type: new GraphQLNonNull( new GraphQLList( new GraphQLInputObjectType({ - name: "FullTextInput", + name: "FulltextInput", fields: { - name: { - deprecationReason, - type: GraphQLString, - }, fields: { type: new GraphQLNonNull(new GraphQLList(GraphQLString)), }, diff --git a/packages/graphql/src/graphql/directives/index.ts b/packages/graphql/src/graphql/directives/index.ts index 594ca4ac19..6dcdfeaa8a 100644 --- a/packages/graphql/src/graphql/directives/index.ts +++ b/packages/graphql/src/graphql/directives/index.ts @@ -33,7 +33,6 @@ export { mutationDirective } from "./mutation"; export { nodeDirective } from "./node"; export { pluralDirective } from "./plural"; export { populatedByDirective } from "./populatedBy"; -export { privateDirective } from "./private"; export { queryDirective } from "./query"; export { relationshipDirective } from "./relationship"; export { relationshipPropertiesDirective } from "./relationship-properties"; @@ -42,5 +41,4 @@ export { selectableDirective } from "./selectable"; export { settableDirective } from "./settable"; export { subscriptionDirective } from "./subscription"; export { timestampDirective } from "./timestamp"; -export { uniqueDirective } from "./unique"; export { vectorDirective } from "./vector"; diff --git a/packages/graphql/src/graphql/directives/relationship.ts b/packages/graphql/src/graphql/directives/relationship.ts index b23a371a2a..17b70c9e6c 100644 --- a/packages/graphql/src/graphql/directives/relationship.ts +++ b/packages/graphql/src/graphql/directives/relationship.ts @@ -50,8 +50,8 @@ export const relationshipDirective = new GraphQLDirective({ }, queryDirection: { type: RelationshipQueryDirectionEnum, - defaultValue: RelationshipQueryDirectionOption.DEFAULT_DIRECTED, - description: "Valid and default directions for this relationship.", + defaultValue: RelationshipQueryDirectionOption.DIRECTED, + description: "Directions to query this relationship.", }, direction: { type: new GraphQLNonNull(RelationshipDirectionEnum), diff --git a/packages/graphql/src/graphql/directives/type-dependant-directives/authorization.ts b/packages/graphql/src/graphql/directives/type-dependant-directives/authorization.ts index dd267a07c8..2765854fdf 100644 --- a/packages/graphql/src/graphql/directives/type-dependant-directives/authorization.ts +++ b/packages/graphql/src/graphql/directives/type-dependant-directives/authorization.ts @@ -18,15 +18,15 @@ */ import { astFromDirective, astFromInputObjectType } from "@graphql-tools/utils"; -import type { TypeDefinitionNode, DirectiveDefinitionNode } from "graphql"; +import type { DirectiveDefinitionNode, TypeDefinitionNode } from "graphql"; import { - GraphQLString, - GraphQLSchema, + DirectiveLocation, + GraphQLBoolean, GraphQLDirective, GraphQLInputObjectType, GraphQLList, - GraphQLBoolean, - DirectiveLocation, + GraphQLSchema, + GraphQLString, } from "graphql"; import { AUTHORIZATION_FILTER_OPERATION, diff --git a/packages/graphql/src/graphql/directives/type-dependant-directives/get-static-auth-definitions.ts b/packages/graphql/src/graphql/directives/type-dependant-directives/get-static-auth-definitions.ts index 5a65b5ca23..f9695d204b 100644 --- a/packages/graphql/src/graphql/directives/type-dependant-directives/get-static-auth-definitions.ts +++ b/packages/graphql/src/graphql/directives/type-dependant-directives/get-static-auth-definitions.ts @@ -62,6 +62,7 @@ export function getStaticAuthorizationDefinitions( const JWTPayloadWhere = createJWTPayloadWhere(userDocument, schema, JWTPayloadDefinition); const JWTPayloadWhereAST = astFromInputObjectType(JWTPayloadWhere, schema); + ASTs.push(JWTPayloadWhereAST); return ASTs; } @@ -80,14 +81,16 @@ function createJWTPayloadWhere( (field) => new AttributeAdapter(parseAttribute(field, definitionCollection)) ); + const composer = new SchemaComposer(); const inputFieldsType = getWhereFieldsForAttributes({ attributes: jwtFieldAttributeAdapters, userDefinedFieldDirectives: undefined, features: undefined, ignoreCypherFieldFilters: false, + composer, }); - const composer = new SchemaComposer(); + const inputTC = composer.createInputTC({ name: "JWTPayloadWhere", fields: inputFieldsType, diff --git a/packages/graphql/src/graphql/directives/vector.ts b/packages/graphql/src/graphql/directives/vector.ts index 914685639f..9a5565f51e 100644 --- a/packages/graphql/src/graphql/directives/vector.ts +++ b/packages/graphql/src/graphql/directives/vector.ts @@ -66,11 +66,6 @@ export const vectorDirective = new GraphQLDirective({ provider: { type: VectorProviderEnum, }, - // callback: { - // description: - // "The name of the callback function that will be used to populate the fields values.", - // type: GraphQLString, - // }, }, }) ) diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/BigIntScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/BigIntScalarAggregationFilters.ts new file mode 100644 index 0000000000..c8f09f3a2f --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/BigIntScalarAggregationFilters.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { BigIntScalarFilters } from "../generic-operators/BigIntScalarFilters"; + +export const BigIntScalarAggregationFilters = new GraphQLInputObjectType({ + name: "BigIntScalarAggregationFilters", + description: "Filters for an aggregation of an BigInt field", + fields: { + average: { type: BigIntScalarFilters }, + max: { type: BigIntScalarFilters }, + min: { type: BigIntScalarFilters }, + sum: { type: BigIntScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DateTimeScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DateTimeScalarAggregationFilters.ts new file mode 100644 index 0000000000..e58747a8f3 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DateTimeScalarAggregationFilters.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { DateTimeScalarFilters } from "../generic-operators/DateTimeScalarFilters"; + +export const DateTimeScalarAggregationFilters = new GraphQLInputObjectType({ + name: "DateTimeScalarAggregationFilters", + description: "Filters for an aggregation of an DateTime input field", + fields: { + max: { type: DateTimeScalarFilters }, + min: { type: DateTimeScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DurationScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DurationScalarAggregationFilters.ts new file mode 100644 index 0000000000..3853c5d534 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/DurationScalarAggregationFilters.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { DurationScalarFilters } from "../generic-operators/DurationScalarFilters"; + +export const DurationScalarAggregationFilters = new GraphQLInputObjectType({ + name: "DurationScalarAggregationFilters", + description: "Filters for an aggregation of a Dutation input field", + fields: { + max: { type: DurationScalarFilters }, + min: { type: DurationScalarFilters }, + average: { type: DurationScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/FloatScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/FloatScalarAggregationFilters.ts new file mode 100644 index 0000000000..2040199bb8 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/FloatScalarAggregationFilters.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { FloatScalarFilters } from "../generic-operators/FloatScalarFilters"; + +export const FloatScalarAggregationFilters = new GraphQLInputObjectType({ + name: "FloatScalarAggregationFilters", + description: "Filters for an aggregation of a float field", + fields: { + average: { type: FloatScalarFilters }, + max: { type: FloatScalarFilters }, + min: { type: FloatScalarFilters }, + sum: { type: FloatScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/IntScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/IntScalarAggregationFilters.ts new file mode 100644 index 0000000000..93f13eebe9 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/IntScalarAggregationFilters.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { FloatScalarFilters } from "../generic-operators/FloatScalarFilters"; +import { IntScalarFilters } from "../generic-operators/IntScalarFilters"; + +export const IntScalarAggregationFilters = new GraphQLInputObjectType({ + name: "IntScalarAggregationFilters", + description: "Filters for an aggregation of an int field", + fields: { + average: { type: FloatScalarFilters }, + max: { type: IntScalarFilters }, + min: { type: IntScalarFilters }, + sum: { type: IntScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalDateTimeScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalDateTimeScalarAggregationFilters.ts new file mode 100644 index 0000000000..f5def1a0ce --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalDateTimeScalarAggregationFilters.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { LocalDateTimeScalarFilters } from "../generic-operators/LocalDateTimeScalarFilters"; + +export const LocalDateTimeScalarAggregationFilters = new GraphQLInputObjectType({ + name: "LocalDateTimeScalarAggregationFilters", + description: "Filters for an aggregation of an LocalDateTime input field", + fields: { + max: { type: LocalDateTimeScalarFilters }, + min: { type: LocalDateTimeScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalTimeScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalTimeScalarAggregationFilters.ts new file mode 100644 index 0000000000..031e769c54 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/LocalTimeScalarAggregationFilters.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { LocalTimeScalarFilters } from "../generic-operators/LocalTimeScalarFilters"; + +export const LocalTimeScalarAggregationFilters = new GraphQLInputObjectType({ + name: "LocalTimeScalarAggregationFilters", + description: "Filters for an aggregation of an LocalTime input field", + fields: { + max: { type: LocalTimeScalarFilters }, + min: { type: LocalTimeScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/StringScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/StringScalarAggregationFilters.ts new file mode 100644 index 0000000000..a3318322f9 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/StringScalarAggregationFilters.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { FloatScalarFilters } from "../generic-operators/FloatScalarFilters"; +import { IntScalarFilters } from "../generic-operators/IntScalarFilters"; + +export const StringScalarAggregationFilters = new GraphQLInputObjectType({ + name: "StringScalarAggregationFilters", + description: "Filters for an aggregation of a string field", + fields: { + averageLength: { type: FloatScalarFilters }, + shortestLength: { type: IntScalarFilters }, + longestLength: { type: IntScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/TimeScalarAggregationFilters.ts b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/TimeScalarAggregationFilters.ts new file mode 100644 index 0000000000..c98495b819 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-aggregation-filters/TimeScalarAggregationFilters.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { TimeScalarFilters } from "../generic-operators/TimeScalarFilters"; + +export const TimeScalarAggregationFilters = new GraphQLInputObjectType({ + name: "TimeScalarAggregationFilters", + description: "Filters for an aggregation of an Time input field", + fields: { + max: { type: TimeScalarFilters }, + min: { type: TimeScalarFilters }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/BigIntScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/BigIntScalarMutations.ts new file mode 100644 index 0000000000..5573c9e377 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/BigIntScalarMutations.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLBigInt } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const BigIntScalarMutations = new GraphQLInputObjectType({ + name: "BigIntScalarMutations", + description: "BigInt mutations", + fields: { + set: { type: GraphQLBigInt }, + add: { type: GraphQLBigInt }, + subtract: { type: GraphQLBigInt }, + }, +}); + +export const BigIntListMutations = listMutation(GraphQLBigInt); diff --git a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.test.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/BooleanScalarMutations.ts similarity index 63% rename from packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.test.ts rename to packages/graphql/src/graphql/input-objects/generic-mutation-operations/BooleanScalarMutations.ts index 6ffef30194..26ef76fd2f 100644 --- a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.test.ts +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/BooleanScalarMutations.ts @@ -17,12 +17,15 @@ * limitations under the License. */ -import { EventEmitter } from "events"; -import { Neo4jGraphQLSubscriptionsDefaultEngine } from "./Neo4jGraphQLSubscriptionsDefaultEngine"; +import { GraphQLBoolean, GraphQLInputObjectType } from "graphql"; +import { listMutation } from "./ListMutation"; -describe("Neo4jGraphQLSubscriptionsDefaultEngine", () => { - test("should construct without arguments", () => { - const plugin = new Neo4jGraphQLSubscriptionsDefaultEngine(); - expect(plugin.events).toBeInstanceOf(EventEmitter); - }); +export const BooleanScalarMutations = new GraphQLInputObjectType({ + name: "BooleanScalarMutations", + description: "Boolean mutations", + fields: { + set: { type: GraphQLBoolean }, + }, }); + +export const BooleanListMutations = listMutation(GraphQLBoolean); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/CartesianPointMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/CartesianPointMutations.ts new file mode 100644 index 0000000000..16ad4cc1f9 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/CartesianPointMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { CartesianPointInput } from "../CartesianPointInput"; +import { listMutation } from "./ListMutation"; + +export const CartesianPointMutations = new GraphQLInputObjectType({ + name: "CartesianPointMutations", + description: "CartesianPoint mutations", + fields: { + set: { type: CartesianPointInput }, + }, +}); + +export const CartesianPointListMutations = listMutation(CartesianPointInput); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateScalarMutations.ts new file mode 100644 index 0000000000..82339d2c5c --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLDate } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const DateScalarMutations = new GraphQLInputObjectType({ + name: "DateScalarMutations", + description: "Date mutations", + fields: { + set: { type: GraphQLDate }, + }, +}); + +export const DateListMutations = listMutation(GraphQLDate); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateTimeScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateTimeScalarMutations.ts new file mode 100644 index 0000000000..8b898c8f97 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DateTimeScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLDateTime } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const DateTimeScalarMutations = new GraphQLInputObjectType({ + name: "DateTimeScalarMutations", + description: "DateTime mutations", + fields: { + set: { type: GraphQLDateTime }, + }, +}); + +export const DateTimeListMutations = listMutation(GraphQLDateTime); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DurationScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DurationScalarMutations.ts new file mode 100644 index 0000000000..788cbaaa17 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/DurationScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLDuration } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const DurationScalarMutations = new GraphQLInputObjectType({ + name: "DurationScalarMutations", + description: "Duration mutations", + fields: { + set: { type: GraphQLDuration }, + }, +}); + +export const DurationListMutations = listMutation(GraphQLDuration); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/FloatScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/FloatScalarMutations.ts new file mode 100644 index 0000000000..5a5b9615dd --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/FloatScalarMutations.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLFloat, GraphQLInputObjectType } from "graphql"; +import { listMutation } from "./ListMutation"; + +export const FloatScalarMutations = new GraphQLInputObjectType({ + name: "FloatScalarMutations", + description: "Float mutations", + fields: { + set: { type: GraphQLFloat }, + add: { type: GraphQLFloat }, + subtract: { type: GraphQLFloat }, + multiply: { type: GraphQLFloat }, + divide: { type: GraphQLFloat }, + }, +}); + +export const FloatListMutations = listMutation(GraphQLFloat); diff --git a/packages/graphql/src/graphql/directives/private.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/IDScalarMutations.ts similarity index 65% rename from packages/graphql/src/graphql/directives/private.ts rename to packages/graphql/src/graphql/input-objects/generic-mutation-operations/IDScalarMutations.ts index 622880d2d3..a754ee575e 100644 --- a/packages/graphql/src/graphql/directives/private.ts +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/IDScalarMutations.ts @@ -17,10 +17,15 @@ * limitations under the License. */ -import { DirectiveLocation, GraphQLDirective } from "graphql"; +import { GraphQLID, GraphQLInputObjectType } from "graphql"; +import { listMutation } from "./ListMutation"; -export const privateDirective = new GraphQLDirective({ - name: "private", - description: "Instructs @neo4j/graphql to only expose a field through the Neo4j GraphQL OGM.", - locations: [DirectiveLocation.FIELD_DEFINITION], +export const IDScalarMutations = new GraphQLInputObjectType({ + name: "IDScalarMutations", + description: "ID mutations", + fields: { + set: { type: GraphQLID }, + }, }); + +export const IDListMutations = listMutation(GraphQLID); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/IntScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/IntScalarMutations.ts new file mode 100644 index 0000000000..0d367803a7 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/IntScalarMutations.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLInt } from "graphql"; +import { listMutation } from "./ListMutation"; + +export const IntScalarMutations = new GraphQLInputObjectType({ + name: "IntScalarMutations", + description: "Int mutations", + fields: { + set: { type: GraphQLInt }, + add: { type: GraphQLInt }, + subtract: { type: GraphQLInt }, + }, +}); + +export const IntListMutations = listMutation(GraphQLInt); diff --git a/packages/graphql/src/graphql/directives/unique.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/ListMutation.ts similarity index 51% rename from packages/graphql/src/graphql/directives/unique.ts rename to packages/graphql/src/graphql/input-objects/generic-mutation-operations/ListMutation.ts index 0a0e84b9c1..538d218861 100644 --- a/packages/graphql/src/graphql/directives/unique.ts +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/ListMutation.ts @@ -17,18 +17,17 @@ * limitations under the License. */ -import { DirectiveLocation, GraphQLDirective, GraphQLString } from "graphql"; +import type { GraphQLScalarType } from "graphql"; +import { GraphQLInputObjectType, GraphQLInt, GraphQLList, GraphQLNonNull } from "graphql"; -export const uniqueDirective = new GraphQLDirective({ - name: "unique", - description: - "Informs @neo4j/graphql that there should be a uniqueness constraint in the database for the decorated field.", - locations: [DirectiveLocation.FIELD_DEFINITION], - args: { - constraintName: { - description: - "The name which should be used for this constraint. By default; type name, followed by an underscore, followed by the field name.", - type: GraphQLString, +export function listMutation(inputObject: GraphQLInputObjectType | GraphQLScalarType): GraphQLInputObjectType { + return new GraphQLInputObjectType({ + name: `List${inputObject.name}Mutations`, + description: `Mutations for a list for ${inputObject.name}`, + fields: { + set: { type: new GraphQLList(new GraphQLNonNull(inputObject)) }, + push: { type: new GraphQLList(new GraphQLNonNull(inputObject)) }, + pop: { type: GraphQLInt }, }, - }, -}); + }); +} diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalDateTimeScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalDateTimeScalarMutations.ts new file mode 100644 index 0000000000..f1eea3a6b6 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalDateTimeScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLLocalDateTime } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const LocalDateTimeScalarMutations = new GraphQLInputObjectType({ + name: "LocalDateTimeScalarMutations", + description: "LocalDateTime mutations", + fields: { + set: { type: GraphQLLocalDateTime }, + }, +}); + +export const LocalDateTimeListMutations = listMutation(GraphQLLocalDateTime); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalTimeScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalTimeScalarMutations.ts new file mode 100644 index 0000000000..d9e478c4c9 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/LocalTimeScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLLocalTime } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const LocalTimeScalarMutations = new GraphQLInputObjectType({ + name: "LocalTimeScalarMutations", + description: "LocalTime mutations", + fields: { + set: { type: GraphQLLocalTime }, + }, +}); + +export const LocalTimeListMutations = listMutation(GraphQLLocalTime); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/PointMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/PointMutations.ts new file mode 100644 index 0000000000..873a0d9364 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/PointMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { PointInput } from "../PointInput"; +import { listMutation } from "./ListMutation"; + +export const PointMutations = new GraphQLInputObjectType({ + name: "PointMutations", + description: "Point mutations", + fields: { + set: { type: PointInput }, + }, +}); + +export const PointListMutations = listMutation(PointInput); diff --git a/packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/StringScalarMutations.ts similarity index 63% rename from packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts rename to packages/graphql/src/graphql/input-objects/generic-mutation-operations/StringScalarMutations.ts index 367f69ff21..8ac4c3e54c 100644 --- a/packages/graphql/src/schema-model/annotation/UniqueAnnotation.ts +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/StringScalarMutations.ts @@ -17,13 +17,15 @@ * limitations under the License. */ -import type { Annotation } from "./Annotation"; +import { GraphQLInputObjectType, GraphQLString } from "graphql"; +import { listMutation } from "./ListMutation"; -export class UniqueAnnotation implements Annotation { - readonly name = "unique"; - public readonly constraintName?: string; +export const StringScalarMutations = new GraphQLInputObjectType({ + name: "StringScalarMutations", + description: "String mutations", + fields: { + set: { type: GraphQLString }, + }, +}); - constructor({ constraintName }: { constraintName?: string }) { - this.constraintName = constraintName; - } -} +export const StringListMutations = listMutation(GraphQLString); diff --git a/packages/graphql/src/graphql/input-objects/generic-mutation-operations/TimeScalarMutations.ts b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/TimeScalarMutations.ts new file mode 100644 index 0000000000..51d9d12e82 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-mutation-operations/TimeScalarMutations.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType } from "graphql"; +import { GraphQLTime } from "../../scalars"; +import { listMutation } from "./ListMutation"; + +export const TimeScalarMutations = new GraphQLInputObjectType({ + name: "TimeScalarMutations", + description: "Time mutations", + fields: { + set: { type: GraphQLTime }, + }, +}); + +export const TimeListMutations = listMutation(GraphQLTime); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/BigIntScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/BigIntScalarFilters.ts new file mode 100644 index 0000000000..98f8b10810 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/BigIntScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLBigInt } from "../../scalars"; + +export const BigIntScalarFilters = new GraphQLInputObjectType({ + name: "BigIntScalarFilters", + description: "BigInt filters", + fields: { + eq: { + type: GraphQLBigInt, + }, + gt: { type: GraphQLBigInt }, + gte: { type: GraphQLBigInt }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLBigInt)) }, + lt: { type: GraphQLBigInt }, + lte: { type: GraphQLBigInt }, + }, +}); + +export const BigIntListFilters = new GraphQLInputObjectType({ + name: "BigIntListFilters", + description: "BigInt list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLBigInt)) }, + includes: { type: GraphQLBigInt }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/BooleanScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/BooleanScalarFilters.ts new file mode 100644 index 0000000000..d067cc2e19 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/BooleanScalarFilters.ts @@ -0,0 +1,38 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLBoolean, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; + +export const BooleanScalarFilters = new GraphQLInputObjectType({ + name: "BooleanScalarFilters", + description: "Boolean filters", + fields: { + eq: { + type: GraphQLBoolean, + }, + }, +}); + +export const BooleanListFilters = new GraphQLInputObjectType({ + name: "BooleanListFilters", + description: "Boolean list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLBoolean)) }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/CartesianPointFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/CartesianPointFilters.ts new file mode 100644 index 0000000000..d3e7f35b3c --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/CartesianPointFilters.ts @@ -0,0 +1,56 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLFloat, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { CartesianPointInput } from "../CartesianPointInput"; + +const CartesianDistancePointFilters = new GraphQLInputObjectType({ + name: "CartesianDistancePointFilters", + description: "Distance filters for cartesian points", + fields: { + from: { + type: new GraphQLNonNull(CartesianPointInput), + }, + gt: { type: GraphQLFloat }, + gte: { type: GraphQLFloat }, + lt: { type: GraphQLFloat }, + lte: { type: GraphQLFloat }, + }, +}); + +export const CartesianPointFilters = new GraphQLInputObjectType({ + name: "CartesianPointFilters", + description: "Cartesian Point filters", + fields: { + eq: { + type: CartesianPointInput, + }, + in: { type: new GraphQLList(new GraphQLNonNull(CartesianPointInput)) }, + distance: { type: CartesianDistancePointFilters }, + }, +}); + +export const CartesianPointListFilters = new GraphQLInputObjectType({ + name: "CartesianPointListFilters", + description: "CartesianPoint list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(CartesianPointInput)) }, + includes: { type: CartesianPointInput }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/DateScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/DateScalarFilters.ts new file mode 100644 index 0000000000..99babb4d24 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/DateScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLDate } from "../../scalars"; + +export const DateScalarFilters = new GraphQLInputObjectType({ + name: "DateScalarFilters", + description: "Date filters", + fields: { + eq: { + type: GraphQLDate, + }, + gt: { type: GraphQLDate }, + gte: { type: GraphQLDate }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLDate)) }, + lt: { type: GraphQLDate }, + lte: { type: GraphQLDate }, + }, +}); + +export const DateListFilters = new GraphQLInputObjectType({ + name: "DateListFilters", + description: "Date list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLDate)) }, + includes: { type: GraphQLDate }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/DateTimeScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/DateTimeScalarFilters.ts new file mode 100644 index 0000000000..4fca7078e8 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/DateTimeScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLDateTime } from "../../scalars"; + +export const DateTimeScalarFilters = new GraphQLInputObjectType({ + name: "DateTimeScalarFilters", + description: "DateTime filters", + fields: { + eq: { + type: GraphQLDateTime, + }, + gt: { type: GraphQLDateTime }, + gte: { type: GraphQLDateTime }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLDateTime)) }, + lt: { type: GraphQLDateTime }, + lte: { type: GraphQLDateTime }, + }, +}); + +export const DateTimeListFilters = new GraphQLInputObjectType({ + name: "DateTimeListFilters", + description: "DateTime list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLDateTime)) }, + includes: { type: GraphQLDateTime }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/DurationScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/DurationScalarFilters.ts new file mode 100644 index 0000000000..358b723853 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/DurationScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLDuration } from "../../scalars"; + +export const DurationScalarFilters = new GraphQLInputObjectType({ + name: "DurationScalarFilters", + description: "Duration filters", + fields: { + eq: { + type: GraphQLDuration, + }, + gt: { type: GraphQLDuration }, + gte: { type: GraphQLDuration }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLDuration)) }, + lt: { type: GraphQLDuration }, + lte: { type: GraphQLDuration }, + }, +}); + +export const DurationListFilters = new GraphQLInputObjectType({ + name: "DurationListFilters", + description: "Duration list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLDuration)) }, + includes: { type: GraphQLDuration }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/FloatScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/FloatScalarFilters.ts new file mode 100644 index 0000000000..1e57f860da --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/FloatScalarFilters.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLFloat, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; + +export const FloatScalarFilters = new GraphQLInputObjectType({ + name: "FloatScalarFilters", + description: "Float filters", + fields: { + eq: { + type: GraphQLFloat, + }, + gt: { type: GraphQLFloat }, + gte: { type: GraphQLFloat }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)) }, + lt: { type: GraphQLFloat }, + lte: { type: GraphQLFloat }, + }, +}); + +export const FloatListFilters = new GraphQLInputObjectType({ + name: "FloatListFilters", + description: "Float list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLFloat)) }, + includes: { type: GraphQLFloat }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/IDScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/IDScalarFilters.ts new file mode 100644 index 0000000000..07500a0f2d --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/IDScalarFilters.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLID, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import type { Neo4jFeaturesSettings } from "../../../types"; + +export function getIDScalarFilters(features?: Neo4jFeaturesSettings): GraphQLInputObjectType { + const fields = { + eq: { + type: GraphQLID, + }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLID)) }, + contains: { type: GraphQLID }, + endsWith: { type: GraphQLID }, + startsWith: { type: GraphQLID }, + }; + for (const filter of Object.entries(features?.filters?.ID ?? {})) { + const [filterName, isEnabled] = filter; + if (isEnabled) { + switch (filterName) { + case "MATCHES": + fields["matches"] = { type: GraphQLID }; + break; + case "GT": + fields["gt"] = { type: GraphQLID }; + break; + case "GTE": + fields["gte"] = { type: GraphQLID }; + break; + case "LT": + fields["lt"] = { type: GraphQLID }; + break; + case "LTE": + fields["lte"] = { type: GraphQLID }; + break; + } + } + } + return new GraphQLInputObjectType({ + name: "IDScalarFilters", + description: "ID filters", + fields, + }); +} + +export const IDListFilters = new GraphQLInputObjectType({ + name: "IDListFilters", + description: "ID list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLID)) }, + includes: { type: GraphQLID }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/IntScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/IntScalarFilters.ts new file mode 100644 index 0000000000..94b9aaa611 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/IntScalarFilters.ts @@ -0,0 +1,42 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLInt, GraphQLList, GraphQLNonNull } from "graphql"; + +export const IntScalarFilters = new GraphQLInputObjectType({ + name: "IntScalarFilters", + description: "Int filters", + fields: { + eq: { type: GraphQLInt }, + gt: { type: GraphQLInt }, + gte: { type: GraphQLInt }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) }, + lt: { type: GraphQLInt }, + lte: { type: GraphQLInt }, + }, +}); + +export const IntListFilters = new GraphQLInputObjectType({ + name: "IntListFilters", + description: "Int list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) }, + includes: { type: GraphQLInt }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/LocalDateTimeScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/LocalDateTimeScalarFilters.ts new file mode 100644 index 0000000000..646f5f2d13 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/LocalDateTimeScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLLocalDateTime } from "../../scalars"; + +export const LocalDateTimeScalarFilters = new GraphQLInputObjectType({ + name: "LocalDateTimeScalarFilters", + description: "LocalDateTime filters", + fields: { + eq: { + type: GraphQLLocalDateTime, + }, + gt: { type: GraphQLLocalDateTime }, + gte: { type: GraphQLLocalDateTime }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLLocalDateTime)) }, + lt: { type: GraphQLLocalDateTime }, + lte: { type: GraphQLLocalDateTime }, + }, +}); + +export const LocalDateTimeListFilters = new GraphQLInputObjectType({ + name: "LocalDateTimeListFilters", + description: "LocalDateTime list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLLocalDateTime)) }, + includes: { type: GraphQLLocalDateTime }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/LocalTimeScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/LocalTimeScalarFilters.ts new file mode 100644 index 0000000000..5c57ddf7ea --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/LocalTimeScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLLocalTime } from "../../scalars"; + +export const LocalTimeScalarFilters = new GraphQLInputObjectType({ + name: "LocalTimeScalarFilters", + description: "LocalTime filters", + fields: { + eq: { + type: GraphQLLocalTime, + }, + gt: { type: GraphQLLocalTime }, + gte: { type: GraphQLLocalTime }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLLocalTime)) }, + lt: { type: GraphQLLocalTime }, + lte: { type: GraphQLLocalTime }, + }, +}); + +export const LocalTimeListFilters = new GraphQLInputObjectType({ + name: "LocalTimeListFilters", + description: "LocalTime list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLLocalTime)) }, + includes: { type: GraphQLLocalTime }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/PointFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/PointFilters.ts new file mode 100644 index 0000000000..0e3bfba6f2 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/PointFilters.ts @@ -0,0 +1,57 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLFloat, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { PointInput } from "../PointInput"; + +const DistancePointFilters = new GraphQLInputObjectType({ + name: "PointDistanceFilters", + description: "Distance filters", + fields: { + from: { + type: new GraphQLNonNull(PointInput), + }, + gt: { type: GraphQLFloat }, + gte: { type: GraphQLFloat }, + lt: { type: GraphQLFloat }, + lte: { type: GraphQLFloat }, + eq: { type: GraphQLFloat }, + }, +}); + +export const PointFilters = new GraphQLInputObjectType({ + name: "PointFilters", + description: "Point filters", + fields: { + eq: { + type: PointInput, + }, + in: { type: new GraphQLList(new GraphQLNonNull(PointInput)) }, + distance: { type: DistancePointFilters }, + }, +}); + +export const PointListFilters = new GraphQLInputObjectType({ + name: "PointListFilters", + description: "Point list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(PointInput)) }, + includes: { type: PointInput }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/StringScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/StringScalarFilters.ts new file mode 100644 index 0000000000..4840b2b3d6 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/StringScalarFilters.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql"; +import type { Neo4jFeaturesSettings } from "../../../types"; + +export function getStringScalarFilters(features?: Neo4jFeaturesSettings): GraphQLInputObjectType { + const fields = { + eq: { + type: GraphQLString, + }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) }, + contains: { type: GraphQLString }, + endsWith: { type: GraphQLString }, + startsWith: { type: GraphQLString }, + }; + for (const filter of Object.entries(features?.filters?.String ?? {})) { + const [filterName, isEnabled] = filter; + if (isEnabled) { + switch (filterName) { + case "MATCHES": + fields["matches"] = { type: GraphQLString }; + break; + case "GT": + fields["gt"] = { type: GraphQLString }; + break; + case "GTE": + fields["gte"] = { type: GraphQLString }; + break; + case "LT": + fields["lt"] = { type: GraphQLString }; + break; + case "LTE": + fields["lte"] = { type: GraphQLString }; + break; + } + } + } + return new GraphQLInputObjectType({ + name: "StringScalarFilters", + description: "String filters", + fields, + }); +} + +export const StringListFilters = new GraphQLInputObjectType({ + name: "StringListFilters", + description: "String list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) }, + includes: { type: GraphQLString }, + }, +}); diff --git a/packages/graphql/src/graphql/input-objects/generic-operators/TimeScalarFilters.ts b/packages/graphql/src/graphql/input-objects/generic-operators/TimeScalarFilters.ts new file mode 100644 index 0000000000..a6973485f9 --- /dev/null +++ b/packages/graphql/src/graphql/input-objects/generic-operators/TimeScalarFilters.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from "graphql"; +import { GraphQLTime } from "../../scalars"; + +export const TimeScalarFilters = new GraphQLInputObjectType({ + name: "TimeScalarFilters", + description: "Time filters", + fields: { + eq: { + type: GraphQLTime, + }, + gt: { type: GraphQLTime }, + gte: { type: GraphQLTime }, + in: { type: new GraphQLList(new GraphQLNonNull(GraphQLTime)) }, + lt: { type: GraphQLTime }, + lte: { type: GraphQLTime }, + }, +}); + +export const TimeListFilters = new GraphQLInputObjectType({ + name: "TimeListFilters", + description: "Time list filters", + fields: { + eq: { type: new GraphQLList(new GraphQLNonNull(GraphQLTime)) }, + includes: { type: GraphQLTime }, + }, +}); diff --git a/packages/graphql/src/graphql/scalars/DateTime.ts b/packages/graphql/src/graphql/scalars/DateTime.ts index 30b3311235..e726d4c71c 100644 --- a/packages/graphql/src/graphql/scalars/DateTime.ts +++ b/packages/graphql/src/graphql/scalars/DateTime.ts @@ -19,7 +19,8 @@ import type { ValueNode } from "graphql"; import { GraphQLError, GraphQLScalarType, Kind } from "graphql"; -import neo4j, { isDateTime } from "neo4j-driver"; +import type neo4j from "neo4j-driver"; +import { isDateTime } from "neo4j-driver"; export const GraphQLDateTime = new GraphQLScalarType({ name: "DateTime", @@ -43,10 +44,6 @@ export const GraphQLDateTime = new GraphQLScalarType({ throw new GraphQLError(`DateTime cannot represent non temporal value: ${inputValue}`); } - return neo4j.types.DateTime.fromStandardDate(date); - } - - if (isDateTime(inputValue)) { return inputValue; } @@ -57,6 +54,6 @@ export const GraphQLDateTime = new GraphQLScalarType({ throw new GraphQLError("DateTime cannot represent non string value."); } - return neo4j.types.DateTime.fromStandardDate(new Date(ast.value)); + return ast.value; }, }); diff --git a/packages/graphql/src/graphql/scalars/Time.test.ts b/packages/graphql/src/graphql/scalars/Time.test.ts deleted file mode 100644 index ea94f95e40..0000000000 --- a/packages/graphql/src/graphql/scalars/Time.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { parseTime } from "./Time"; - -describe("Time Scalar", () => { - describe("parseTime", () => { - test.each(["22:20:00", "22:20"])("should properly parse %s", (input: string) => { - const parsedTime = parseTime(input); - expect(parsedTime).toEqual({ - hour: 22, - minute: 20, - second: 0, - nanosecond: 0, - timeZoneOffsetSeconds: 0, - }); - }); - - test("should properly parse time in RFC3339 format", () => { - const parsedTime = parseTime("22:10:15.555-01:02"); - - expect(parsedTime).toEqual({ - hour: 22, - minute: 10, - second: 15, - nanosecond: 555000000, - timeZoneOffsetSeconds: -3720, - }); - }); - - test.each(["22", "22:00.555"])("should not parse %s", (input: string) => { - expect(() => parseTime(input)).toThrow(); - }); - }); -}); diff --git a/packages/graphql/src/graphql/scalars/Time.ts b/packages/graphql/src/graphql/scalars/Time.ts index 6f68e6bc49..9d2eeeeb49 100644 --- a/packages/graphql/src/graphql/scalars/Time.ts +++ b/packages/graphql/src/graphql/scalars/Time.ts @@ -19,30 +19,12 @@ import type { ValueNode } from "graphql"; import { GraphQLError, GraphQLScalarType, Kind } from "graphql"; -import neo4j, { isTime } from "neo4j-driver"; +import neo4j from "neo4j-driver"; export const TIME_REGEX = /^(?[01]\d|2[0-3]):(?[0-5]\d)(:(?[0-5]\d)(\.(?\d{1}(?:\d{0,8})))?((?:[Zz])|((?[-|+])(?[01]\d|2[0-3]):(?[0-5]\d)))?)?$/; -type TimeRegexMatchGroups = { - hour: string; - minute: string; - second: string; - fraction: string; - offsetDirection: string; - offsetHour: string; - offsetMinute: string; -}; - -type ParsedTime = { - hour: number; - minute: number; - second: number; - nanosecond: number; - timeZoneOffsetSeconds: number; -}; - -export const parseTime = (value: unknown): ParsedTime => { +export const validateTime = (value: unknown): string => { if (typeof value !== "string") { throw new TypeError(`Value must be of type string: ${value}`); } @@ -53,39 +35,7 @@ export const parseTime = (value: unknown): ParsedTime => { throw new TypeError(`Value must be formatted as Time: ${value}`); } - const { hour, minute, second, fraction, offsetDirection, offsetHour, offsetMinute } = - match.groups as TimeRegexMatchGroups; - // Calculate the number of nanoseconds by padding the fraction of seconds with zeroes to nine digits - let nanosecond = 0; - if (fraction) { - nanosecond = +`${fraction}000000000`.substring(0, 9); - } - - // Calculate the timeZoneOffsetSeconds by calculating the offset in seconds with the appropriate sign - let timeZoneOffsetSeconds = 0; - if (offsetDirection && offsetHour && offsetMinute) { - const offsetInMinutes = +offsetMinute + +offsetHour * 60; - const offsetInSeconds = offsetInMinutes * 60; - timeZoneOffsetSeconds = +`${offsetDirection}${offsetInSeconds}`; - } - - return { - hour: +hour, - minute: +minute, - second: +(second || 0), - nanosecond, - timeZoneOffsetSeconds, - }; -}; - -const parse = (value: unknown) => { - if (isTime(value)) { - return value; - } - - const { hour, minute, second, nanosecond, timeZoneOffsetSeconds } = parseTime(value); - - return new neo4j.types.Time(hour, minute, second, nanosecond, timeZoneOffsetSeconds); + return value; }; export const GraphQLTime = new GraphQLScalarType({ @@ -105,12 +55,12 @@ export const GraphQLTime = new GraphQLScalarType({ return stringifiedValue; }, parseValue: (value: unknown) => { - return parse(value); + return validateTime(value); }, parseLiteral: (ast: ValueNode) => { if (ast.kind !== Kind.STRING) { throw new GraphQLError(`Only strings can be validated as Time, but received: ${ast.kind}`); } - return parse(ast.value); + return validateTime(ast.value); }, }); diff --git a/packages/graphql/src/schema-model/Neo4jGraphQLSchemaModel.ts b/packages/graphql/src/schema-model/Neo4jGraphQLSchemaModel.ts index 82e9a60315..b1e4ab3cf2 100644 --- a/packages/graphql/src/schema-model/Neo4jGraphQLSchemaModel.ts +++ b/packages/graphql/src/schema-model/Neo4jGraphQLSchemaModel.ts @@ -70,8 +70,7 @@ export class Neo4jGraphQLSchemaModel { } public getConcreteEntity(name: string): ConcreteEntity | undefined { - const concreteEntity = this.concreteEntities.find((entity) => entity.name === name); - return concreteEntity; + return this.concreteEntities.find((entity) => entity.name === name); } public getEntitiesByLabels(labels: string[]): ConcreteEntity[] { diff --git a/packages/graphql/src/schema-model/annotation/Annotation.ts b/packages/graphql/src/schema-model/annotation/Annotation.ts index c495d6b59e..25751e187c 100644 --- a/packages/graphql/src/schema-model/annotation/Annotation.ts +++ b/packages/graphql/src/schema-model/annotation/Annotation.ts @@ -25,7 +25,7 @@ import { parseCustomResolverAnnotation } from "../parser/annotations-parser/cust import { parseCypherAnnotation } from "../parser/annotations-parser/cypher-annotation"; import { parseDefaultAnnotation } from "../parser/annotations-parser/default-annotation"; import { parseFilterableAnnotation } from "../parser/annotations-parser/filterable-annotation"; -import { parseFullTextAnnotation } from "../parser/annotations-parser/full-text-annotation"; +import { parseFulltextAnnotation } from "../parser/annotations-parser/full-text-annotation"; import { parseJWTClaimAnnotation } from "../parser/annotations-parser/jwt-claim-annotation"; import { parseKeyAnnotation } from "../parser/annotations-parser/key-annotation"; import { parseLimitAnnotation } from "../parser/annotations-parser/limit-annotation"; @@ -38,7 +38,6 @@ import { parseSettableAnnotation } from "../parser/annotations-parser/settable-a import { parseSubscriptionAnnotation } from "../parser/annotations-parser/subscription-annotation"; import { parseSubscriptionsAuthorizationAnnotation } from "../parser/annotations-parser/subscriptions-authorization-annotation"; import { parseTimestampAnnotation } from "../parser/annotations-parser/timestamp-annotation"; -import { parseUniqueAnnotation } from "../parser/annotations-parser/unique-annotation"; import { parseVectorAnnotation } from "../parser/annotations-parser/vector-annotation"; import type { AuthenticationAnnotation } from "./AuthenticationAnnotation"; import type { AuthorizationAnnotation } from "./AuthorizationAnnotation"; @@ -47,7 +46,7 @@ import type { CustomResolverAnnotation } from "./CustomResolverAnnotation"; import type { CypherAnnotation } from "./CypherAnnotation"; import type { DefaultAnnotation } from "./DefaultAnnotation"; import type { FilterableAnnotation } from "./FilterableAnnotation"; -import type { FullTextAnnotation } from "./FullTextAnnotation"; +import type { FulltextAnnotation } from "./FulltextAnnotation"; import { IDAnnotation } from "./IDAnnotation"; import type { JWTClaimAnnotation } from "./JWTClaimAnnotation"; import { JWTPayloadAnnotation } from "./JWTPayloadAnnotation"; @@ -64,7 +63,6 @@ import type { SettableAnnotation } from "./SettableAnnotation"; import type { SubscriptionAnnotation } from "./SubscriptionAnnotation"; import type { SubscriptionsAuthorizationAnnotation } from "./SubscriptionsAuthorizationAnnotation"; import type { TimestampAnnotation } from "./TimestampAnnotation"; -import type { UniqueAnnotation } from "./UniqueAnnotation"; import type { VectorAnnotation } from "./VectorAnnotation"; export interface Annotation { @@ -84,7 +82,7 @@ export type Annotations = CheckAnnotationName<{ cypher: CypherAnnotation; default: DefaultAnnotation; filterable: FilterableAnnotation; - fulltext: FullTextAnnotation; + fulltext: FulltextAnnotation; vector: VectorAnnotation; id: IDAnnotation; jwt: JWTPayloadAnnotation; @@ -102,7 +100,6 @@ export type Annotations = CheckAnnotationName<{ subscription: SubscriptionAnnotation; subscriptionsAuthorization: SubscriptionsAuthorizationAnnotation; timestamp: TimestampAnnotation; - unique: UniqueAnnotation; }>; export type AnnotationParser = ( @@ -118,7 +115,7 @@ export const annotationsParsers: { [key in keyof Annotations]: AnnotationParser< cypher: parseCypherAnnotation, default: parseDefaultAnnotation, filterable: parseFilterableAnnotation, - fulltext: parseFullTextAnnotation, + fulltext: parseFulltextAnnotation, id: () => new IDAnnotation(), jwtClaim: parseJWTClaimAnnotation, jwt: () => new JWTPayloadAnnotation(), @@ -134,7 +131,6 @@ export const annotationsParsers: { [key in keyof Annotations]: AnnotationParser< subscription: parseSubscriptionAnnotation, subscriptionsAuthorization: parseSubscriptionsAuthorizationAnnotation, timestamp: parseTimestampAnnotation, - unique: parseUniqueAnnotation, relayId: () => new RelayIDAnnotation(), vector: parseVectorAnnotation, }; diff --git a/packages/graphql/src/schema-model/annotation/CustomResolverAnnotation.ts b/packages/graphql/src/schema-model/annotation/CustomResolverAnnotation.ts index 9a56f056c0..2f32b87267 100644 --- a/packages/graphql/src/schema-model/annotation/CustomResolverAnnotation.ts +++ b/packages/graphql/src/schema-model/annotation/CustomResolverAnnotation.ts @@ -19,9 +19,9 @@ import type { DocumentNode, FieldDefinitionNode } from "graphql"; import { parse } from "graphql"; -import { selectionSetToResolveTree } from "../../schema/get-custom-resolver-meta"; -import { getDefinitionNodes } from "../../schema/get-definition-nodes"; import type { ResolveTree } from "graphql-parse-resolve-info"; +import { selectionSetToResolveTree } from "../../schema/get-custom-resolver-meta"; +import { getDefinitionCollection } from "../parser/definition-collection"; import type { Annotation } from "./Annotation"; export class CustomResolverAnnotation implements Annotation { @@ -37,16 +37,18 @@ export class CustomResolverAnnotation implements Annotation { if (!this.requires) { return; } - const definitionNodes = getDefinitionNodes(document); + const definitionCollection = getDefinitionCollection(document); - const { interfaceTypes, objectTypes, unionTypes } = definitionNodes; + const { interfaceTypes, objectTypes, unionTypes } = definitionCollection; const selectionSetDocument = parse(`{ ${this.requires} }`); + // TODO: likely selectionSetToResolveTree could be change to accept Maps instead of Arrays. + // initially these were arrays as they were coming from getDefinitionNodes that was returning arrays this.parsedRequires = selectionSetToResolveTree( objectFields || [], - objectTypes, - interfaceTypes, - unionTypes, + [...objectTypes.values()], + [...interfaceTypes.values()], + [...unionTypes.values()], selectionSetDocument ); } diff --git a/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts b/packages/graphql/src/schema-model/annotation/FulltextAnnotation.ts similarity index 77% rename from packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts rename to packages/graphql/src/schema-model/annotation/FulltextAnnotation.ts index 66a132019c..10bcee60c8 100644 --- a/packages/graphql/src/schema-model/annotation/FullTextAnnotation.ts +++ b/packages/graphql/src/schema-model/annotation/FulltextAnnotation.ts @@ -19,18 +19,17 @@ import type { Annotation } from "./Annotation"; -export type FullTextField = { - name?: string; - fields: string[]; - queryName?: string; +export type FulltextField = { indexName: string; + queryName: string; + fields: string[]; }; -export class FullTextAnnotation implements Annotation { +export class FulltextAnnotation implements Annotation { readonly name = "fulltext"; - public readonly indexes: FullTextField[]; + public readonly indexes: FulltextField[]; - constructor({ indexes }: { indexes: FullTextField[] }) { + constructor({ indexes }: { indexes: FulltextField[] }) { this.indexes = indexes; } } diff --git a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts index 4f84f7898f..3f910d2f0b 100644 --- a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts +++ b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.test.ts @@ -18,7 +18,6 @@ */ import { CypherAnnotation } from "../../annotation/CypherAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../Attribute"; import { EnumType, @@ -555,18 +554,6 @@ describe("Attribute", () => { }); describe("annotation assertions", () => { - test("isUnique", () => { - const attribute = new AttributeAdapter( - new Attribute({ - name: "test", - annotations: { unique: new UniqueAnnotation({ constraintName: "test" }) }, - type: new ScalarType(GraphQLBuiltInScalarType.ID, true), - args: [], - }) - ); - expect(attribute.isUnique()).toBe(true); - }); - test("isCypher", () => { const attribute = new AttributeAdapter( new Attribute({ diff --git a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts index 26e1b252d1..8e994fb030 100644 --- a/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts +++ b/packages/graphql/src/schema-model/attribute/model-adapters/AttributeAdapter.ts @@ -94,25 +94,10 @@ export class AttributeAdapter { ); } - isUnique(): boolean { - return !!this.annotations.unique || this.isGlobalIDAttribute() === true; - } - isCypher(): boolean { return !!this.annotations.cypher; } - isConstrainable(): boolean { - return ( - this.typeHelper.isGraphQLBuiltInScalar() || - this.typeHelper.isUserScalar() || - this.typeHelper.isEnum() || - this.typeHelper.isTemporal() || - this.typeHelper.isSpatial() || - this.typeHelper.isBigInt() - ); - } - isObjectField(): boolean { return ( this.typeHelper.isScalar() || @@ -193,7 +178,7 @@ export class AttributeAdapter { isAggregableField(): boolean { return ( !this.typeHelper.isList() && - //uncomment me on 7.x !this.typeHelper.isID() && + !this.typeHelper.isID() && (this.typeHelper.isScalar() || this.typeHelper.isEnum()) && this.isAggregable() ); @@ -202,7 +187,7 @@ export class AttributeAdapter { isAggregationWhereField(): boolean { if ( this.typeHelper.isList() || - // uncomment me on 7.x this.typeHelper.isID() || + this.typeHelper.isID() || this.typeHelper.isBoolean() || this.typeHelper.isDate() ) { @@ -335,6 +320,7 @@ export class AttributeAdapter { this.isCypher() === false ); } + isAggregationFilterable(): boolean { return ( this.annotations.filterable?.byAggregate !== false && diff --git a/packages/graphql/src/schema-model/attribute/model-adapters/ListFiltersAdapter.ts b/packages/graphql/src/schema-model/attribute/model-adapters/ListFiltersAdapter.ts deleted file mode 100644 index eb223d8b29..0000000000 --- a/packages/graphql/src/schema-model/attribute/model-adapters/ListFiltersAdapter.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import pluralize from "pluralize"; -import type { RelationshipAdapter } from "../../relationship/model-adapters/RelationshipAdapter"; -import type { RelationshipDeclarationAdapter } from "../../relationship/model-adapters/RelationshipDeclarationAdapter"; - -export class ListFiltersAdapter { - readonly relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - - constructor(relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter) { - if (!relationshipAdapter.isList) { - throw new Error("Relationship field is not a list"); - } - this.relationshipAdapter = relationshipAdapter; - } - - getAll(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.name}_ALL`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where all of the related ${pluralize(this.relationshipAdapter.target.name)} match this filter`, - }; - } - - getNone(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.name}_NONE`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where none of the related ${pluralize(this.relationshipAdapter.target.name)} match this filter`, - }; - } - - getSingle(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.name}_SINGLE`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where one of the related ${pluralize(this.relationshipAdapter.target.name)} match this filter`, - }; - } - - getSome(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.name}_SOME`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where some of the related ${pluralize(this.relationshipAdapter.target.name)} match this filter`, - }; - } - - get filters(): { type: string; description: string }[] { - return [this.getAll(), this.getNone(), this.getSingle(), this.getSome()]; - } - - getConnectionAll(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.operations.connectionFieldName}_ALL`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where all of the related ${pluralize( - this.relationshipAdapter.operations.connectionFieldTypename - )} match this filter`, - }; - } - - getConnectionNone(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.operations.connectionFieldName}_NONE`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where none of the related ${pluralize( - this.relationshipAdapter.operations.connectionFieldTypename - )} match this filter`, - }; - } - - getConnectionSingle(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.operations.connectionFieldName}_SINGLE`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where one of the related ${pluralize( - this.relationshipAdapter.operations.connectionFieldTypename - )} match this filter`, - }; - } - - getConnectionSome(): { type: string; description: string } { - return { - type: `${this.relationshipAdapter.operations.connectionFieldName}_SOME`, - description: `Return ${pluralize( - this.relationshipAdapter.source.name - )} where some of the related ${pluralize( - this.relationshipAdapter.operations.connectionFieldTypename - )} match this filter`, - }; - } - - get connectionFilters(): { type: string; description: string }[] { - return [ - this.getConnectionAll(), - this.getConnectionNone(), - this.getConnectionSingle(), - this.getConnectionSome(), - ]; - } -} diff --git a/packages/graphql/src/schema-model/entity/CompositeEntity.ts b/packages/graphql/src/schema-model/entity/CompositeEntity.ts index b82091a1c7..7b59d7e4a8 100644 --- a/packages/graphql/src/schema-model/entity/CompositeEntity.ts +++ b/packages/graphql/src/schema-model/entity/CompositeEntity.ts @@ -22,6 +22,5 @@ import type { Entity } from "./Entity"; /** models the concept of an Abstract Type */ export interface CompositeEntity extends Entity { - readonly name: string; concreteEntities: ConcreteEntity[]; } diff --git a/packages/graphql/src/schema-model/entity/ConcreteEntity.ts b/packages/graphql/src/schema-model/entity/ConcreteEntity.ts index 18d67d5ab8..417f5a8a0d 100644 --- a/packages/graphql/src/schema-model/entity/ConcreteEntity.ts +++ b/packages/graphql/src/schema-model/entity/ConcreteEntity.ts @@ -17,11 +17,13 @@ * limitations under the License. */ +import { Memoize } from "typescript-memoize"; import { Neo4jGraphQLSchemaValidationError } from "../../classes"; import { setsAreEqual } from "../../utils/sets-are-equal"; import type { Annotations } from "../annotation/Annotation"; import type { Attribute } from "../attribute/Attribute"; import type { Relationship } from "../relationship/Relationship"; +import { plural, singular } from "../utils/string-manipulation"; import type { CompositeEntity } from "./CompositeEntity"; import type { Entity } from "./Entity"; @@ -107,4 +109,13 @@ export class ConcreteEntity implements Entity { public findRelationship(name: string): Relationship | undefined { return this.relationships.get(name); } + + // Duplicate in EntityAdapters + @Memoize() + public get plural(): string { + if (this.annotations.plural) { + return singular(this.annotations.plural.value); + } + return plural(this.name); + } } diff --git a/packages/graphql/src/schema-model/entity/Entity.ts b/packages/graphql/src/schema-model/entity/Entity.ts index c856bc5148..813b369867 100644 --- a/packages/graphql/src/schema-model/entity/Entity.ts +++ b/packages/graphql/src/schema-model/entity/Entity.ts @@ -17,11 +17,15 @@ * limitations under the License. */ +import type { Annotations } from "../annotation/Annotation"; import type { CompositeEntity } from "./CompositeEntity"; import type { ConcreteEntity } from "./ConcreteEntity"; export interface Entity { readonly name: string; + readonly annotations: Partial; + + get plural(): string; isConcreteEntity(): this is ConcreteEntity; isCompositeEntity(): this is CompositeEntity; diff --git a/packages/graphql/src/schema-model/entity/InterfaceEntity.ts b/packages/graphql/src/schema-model/entity/InterfaceEntity.ts index 295ffdccf1..7bbe6d06c5 100644 --- a/packages/graphql/src/schema-model/entity/InterfaceEntity.ts +++ b/packages/graphql/src/schema-model/entity/InterfaceEntity.ts @@ -17,6 +17,8 @@ * limitations under the License. */ +import { plural, singular } from "pluralize"; +import { Memoize } from "typescript-memoize"; import { Neo4jGraphQLSchemaValidationError } from "../../classes"; import type { Annotations } from "../annotation/Annotation"; import type { Attribute } from "../attribute/Attribute"; @@ -87,4 +89,13 @@ export class InterfaceEntity implements CompositeEntity { public findAttribute(name: string): Attribute | undefined { return this.attributes.get(name); } + + // Duplicate in EntityAdapters + @Memoize() + public get plural(): string { + if (this.annotations.plural) { + return singular(this.annotations.plural.value); + } + return plural(this.name); + } } diff --git a/packages/graphql/src/schema-model/entity/UnionEntity.ts b/packages/graphql/src/schema-model/entity/UnionEntity.ts index f8ee588f17..2c9ed30fbf 100644 --- a/packages/graphql/src/schema-model/entity/UnionEntity.ts +++ b/packages/graphql/src/schema-model/entity/UnionEntity.ts @@ -17,9 +17,11 @@ * limitations under the License. */ -import type { ConcreteEntity } from "./ConcreteEntity"; -import type { CompositeEntity } from "./CompositeEntity"; +import { plural, singular } from "pluralize"; +import { Memoize } from "typescript-memoize"; import type { Annotations } from "../annotation/Annotation"; +import type { CompositeEntity } from "./CompositeEntity"; +import type { ConcreteEntity } from "./ConcreteEntity"; export class UnionEntity implements CompositeEntity { public readonly name: string; @@ -46,4 +48,13 @@ export class UnionEntity implements CompositeEntity { isCompositeEntity(): this is CompositeEntity { return true; } + + // Duplicate in EntityAdapters + @Memoize() + public get plural(): string { + if (this.annotations.plural) { + return singular(this.annotations.plural.value); + } + return plural(this.name); + } } diff --git a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts index 2887718393..c9ba2cc9ae 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.test.ts @@ -18,7 +18,6 @@ */ import { CypherAnnotation } from "../../annotation/CypherAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../../attribute/Attribute"; import { GraphQLBuiltInScalarType, ScalarType } from "../../attribute/AttributeType"; import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter"; @@ -34,7 +33,7 @@ describe("ConcreteEntityAdapter", () => { beforeAll(() => { const idAttribute = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); @@ -95,11 +94,6 @@ describe("ConcreteEntityAdapter", () => { expect(userAdapter.getMainLabel()).toBe("User"); }); - test("should return the correct unique fields", () => { - expect(userAdapter.uniqueFields).toHaveLength(1); - expect(userAdapter.uniqueFields).toStrictEqual([userId]); - }); - test("should return the correct singular name", () => { expect(userAdapter.singular).toBe("user"); }); diff --git a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts index 5543b19dc9..9c0faeb615 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityAdapter.ts @@ -44,8 +44,6 @@ export class ConcreteEntityAdapter { // These keys allow to store the keys of the map in memory and avoid keep iterating over the map. private mutableFieldsKeys: string[] = []; - private uniqueFieldsKeys: string[] = []; - private constrainableFieldsKeys: string[] = []; private _relatedEntities: EntityAdapter[] | undefined; @@ -78,13 +76,6 @@ export class ConcreteEntityAdapter { this.mutableFieldsKeys.push(attribute.name); } - if (attributeAdapter.isConstrainable()) { - this.constrainableFieldsKeys.push(attribute.name); - if (attributeAdapter.isUnique()) { - this.uniqueFieldsKeys.push(attribute.name); - } - } - if (attributeAdapter.isGlobalIDAttribute()) { this._globalIdField = attributeAdapter; } @@ -157,14 +148,6 @@ export class ConcreteEntityAdapter { return this.mutableFieldsKeys.map((key) => getFromMap(this.attributes, key)); } - public get uniqueFields(): AttributeAdapter[] { - return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - - public get constrainableFields(): AttributeAdapter[] { - return this.constrainableFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - public get relatedEntities(): EntityAdapter[] { if (!this._relatedEntities) { this._relatedEntities = [...this.relationships.values()].map((relationship) => relationship.target); diff --git a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityOperations.ts b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityOperations.ts index 83c3956231..a0ed59c0b7 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityOperations.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/ConcreteEntityOperations.ts @@ -17,7 +17,6 @@ * limitations under the License. */ -import { upperFirst } from "../../../utils/upper-first"; import type { ConcreteEntityAdapter } from "./ConcreteEntityAdapter"; import type { RootTypeFieldNames as ImplementingTypeRootTypeFieldNames } from "./ImplementingEntityOperations"; import { ImplementingEntityOperations } from "./ImplementingEntityOperations"; @@ -31,13 +30,7 @@ type RootTypeFieldNames = ImplementingTypeRootTypeFieldNames & { }; }; -type FulltextTypeNames = { - result: string; - where: string; - sort: string; -}; - -type VectorTypeNames = { +type IndexTypeNames = { connection: string; edge: string; where: string; @@ -49,18 +42,6 @@ export class ConcreteEntityOperations extends ImplementingEntityOperations = new Map(); public readonly relationshipDeclarations: Map = new Map(); public readonly annotations: Partial; - private uniqueFieldsKeys: string[] = []; private _singular: string | undefined; private _plural: string | undefined; @@ -112,10 +110,6 @@ export class InterfaceEntityAdapter { * used to generate different types for the Entity that contains these Attributes */ - public get uniqueFields(): AttributeAdapter[] { - return this.uniqueFieldsKeys.map((key) => getFromMap(this.attributes, key)); - } - public get sortableFields(): AttributeAdapter[] { return Array.from(this.attributes.values()).filter((attribute) => attribute.isSortableField()); } @@ -167,9 +161,6 @@ export class InterfaceEntityAdapter { for (const [attributeName, attribute] of attributes.entries()) { const attributeAdapter = new AttributeAdapter(attribute); this.attributes.set(attributeName, attributeAdapter); - if (attributeAdapter.isConstrainable() && attributeAdapter.isUnique()) { - this.uniqueFieldsKeys.push(attribute.name); - } } } diff --git a/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityOperations.ts b/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityOperations.ts index f7626e90dd..6b85ea3574 100644 --- a/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityOperations.ts +++ b/packages/graphql/src/schema-model/entity/model-adapters/InterfaceEntityOperations.ts @@ -17,7 +17,6 @@ * limitations under the License. */ -import { upperFirst } from "graphql-compose"; import { ImplementingEntityOperations } from "./ImplementingEntityOperations"; import type { InterfaceEntityAdapter } from "./InterfaceEntityAdapter"; @@ -34,14 +33,6 @@ export class InterfaceEntityOperations extends ImplementingEntityOperations { expect(accounts).toBeDefined(); expect(accounts?.type).toBe("HAS_ACCOUNT"); expect(accounts?.direction).toBe("OUT"); - expect(accounts?.queryDirection).toBe("DEFAULT_DIRECTED"); + expect(accounts?.queryDirection).toBe("DIRECTED"); expect(accounts?.nestedOperations).toEqual([ "CREATE", "UPDATE", @@ -427,7 +427,7 @@ describe("Relationship", () => { expect(actors).toBeDefined(); expect(actors?.type).toBe("STARED_IN"); expect(actors?.direction).toBe("OUT"); - expect(actors?.queryDirection).toBe("DEFAULT_DIRECTED"); + expect(actors?.queryDirection).toBe("DIRECTED"); expect(actors?.nestedOperations).toEqual([ "CREATE", "UPDATE", diff --git a/packages/graphql/src/schema-model/generate-model.ts b/packages/graphql/src/schema-model/generate-model.ts index 6219166c8d..f63e90fb59 100644 --- a/packages/graphql/src/schema-model/generate-model.ts +++ b/packages/graphql/src/schema-model/generate-model.ts @@ -25,14 +25,10 @@ import type { UnionTypeDefinitionNode, } from "graphql"; import { Neo4jGraphQLSchemaValidationError } from "../classes"; -import { - declareRelationshipDirective, - nodeDirective, - privateDirective, - relationshipDirective, -} from "../graphql/directives"; +import { declareRelationshipDirective, nodeDirective, relationshipDirective } from "../graphql/directives"; import getFieldTypeMeta from "../schema/get-field-type-meta"; import { getInnerTypeName } from "../schema/validation/custom-rules/utils/utils"; +import { validateSchemaModel } from "../schema/validation/validate-schema-model"; import { isInArray } from "../utils/is-in-array"; import { filterTruthy } from "../utils/utils"; import type { Operations } from "./Neo4jGraphQLSchemaModel"; @@ -73,9 +69,6 @@ export function generateModel(document: DocumentNode): Neo4jGraphQLSchemaModel { ); const concreteEntitiesMap = concreteEntities.reduce((acc, entity) => { - if (acc.has(entity.name)) { - throw new Neo4jGraphQLSchemaValidationError(`Duplicate node ${entity.name}`); - } acc.set(entity.name, entity); return acc; }, new Map()); @@ -112,6 +105,9 @@ export function generateModel(document: DocumentNode): Neo4jGraphQLSchemaModel { operations, annotations, }); + + validateSchemaModel(schema); + definitionCollection.nodes.forEach((def) => hydrateRelationships(def, schema, definitionCollection)); hydrateCypherAnnotations(schema, concreteEntities); @@ -210,11 +206,6 @@ function generateInterfaceEntity( ); const fields = (definition.fields || []).map((fieldDefinition) => { - const isPrivateAttribute = findDirective(fieldDefinition.directives, privateDirective.name); - - if (isPrivateAttribute) { - return; - } const isRelationshipAttribute = findDirective(fieldDefinition.directives, declareRelationshipDirective.name); if (isRelationshipAttribute) { return; @@ -237,21 +228,12 @@ function generateCompositeEntity( entityImplementingTypeNames: string[], concreteEntities: Map ): { name: string; concreteEntities: ConcreteEntity[] } { - const compositeFields = entityImplementingTypeNames.map((type) => { - const concreteEntity = concreteEntities.get(type); - if (!concreteEntity) { - throw new Neo4jGraphQLSchemaValidationError(`Could not find concrete entity with name ${type}`); - } - return concreteEntity; - }); - /* - // This is commented out because is currently possible to have leaf interfaces as demonstrated in the test - // packages/graphql/tests/integration/aggregations/where/node/string.int.test.ts - if (!compositeFields.length) { - throw new Neo4jGraphQLSchemaValidationError( - `Composite entity ${entityDefinitionName} has no concrete entities` - ); - } */ + const compositeFields = filterTruthy( + entityImplementingTypeNames.map((type) => { + return concreteEntities.get(type); + }) + ); + return { name: entityDefinitionName, concreteEntities: compositeFields, @@ -450,11 +432,6 @@ function generateRelationshipField( propertiesTypeName = properties; const fields = (propertyInterface.fields || []).map((fieldDefinition) => { - const isPrivateAttribute = findDirective(fieldDefinition.directives, privateDirective.name); - - if (isPrivateAttribute) { - return; - } return parseAttribute(fieldDefinition, definitionCollection, propertyInterface.fields); }); @@ -535,13 +512,6 @@ function generateConcreteEntity( definitionCollection: DefinitionCollection ): ConcreteEntity { const fields = (definition.fields || []).map((fieldDefinition) => { - // If the attribute is the private directive then - const isPrivateAttribute = findDirective(fieldDefinition.directives, privateDirective.name); - - if (isPrivateAttribute) { - return; - } - const isRelationshipAttribute = findDirective(fieldDefinition.directives, relationshipDirective.name); if (isRelationshipAttribute) { return; @@ -550,7 +520,7 @@ function generateConcreteEntity( }); // schema configuration directives are propagated onto concrete entities - const schemaDirectives = definitionCollection.schemaExtension?.directives?.filter((x) => + const schemaDirectives = definitionCollection.schemaExtensions?.directives?.filter((x) => isInArray(SCHEMA_CONFIGURATION_OBJECT_DIRECTIVES, x.name.value) ); const annotations = parseAnnotations((definition.directives || []).concat(schemaDirectives || [])); diff --git a/packages/graphql/src/schema-model/library-directives.ts b/packages/graphql/src/schema-model/library-directives.ts index 1e56e864b0..d06ff58ca0 100644 --- a/packages/graphql/src/schema-model/library-directives.ts +++ b/packages/graphql/src/schema-model/library-directives.ts @@ -16,11 +16,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { Annotations } from "./annotation/Annotation"; -import { annotationsParsers } from "./annotation/Annotation"; import type { DEPRECATED } from "../constants"; import { SHAREABLE } from "../constants"; import type { ValueOf } from "../utils/value-of"; +import type { Annotations } from "./annotation/Annotation"; +import { annotationsParsers } from "./annotation/Annotation"; const additionalDirectives = [ "alias", @@ -60,7 +60,6 @@ export const FIELD_DIRECTIVES = [ "relayId", "subscriptionsAuthorization", "timestamp", - "unique", "declareRelationship", ...SCHEMA_CONFIGURATION_FIELD_DIRECTIVES, ] as const satisfies readonly LibraryDirectives[]; diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.test.ts b/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.test.ts index 9f3c33affd..52931b0aa0 100644 --- a/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.test.ts +++ b/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.test.ts @@ -19,8 +19,8 @@ import { makeDirectiveNode } from "@graphql-tools/utils"; import type { DirectiveNode } from "graphql"; -import { parseFullTextAnnotation } from "./full-text-annotation"; import { fulltextDirective } from "../../../graphql/directives"; +import { parseFulltextAnnotation } from "./full-text-annotation"; describe("parseFullTextAnnotation", () => { it("should parse correctly", () => { @@ -29,7 +29,7 @@ describe("parseFullTextAnnotation", () => { { indexes: [{ indexName: "ProductName", fields: ["name"] }] }, fulltextDirective ); - const fullTextAnnotation = parseFullTextAnnotation(directive); + const fullTextAnnotation = parseFulltextAnnotation(directive); expect(fullTextAnnotation).toEqual({ name: "fulltext", indexes: [ diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.ts b/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.ts index 9d0f60ad8c..f5acb360c5 100644 --- a/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.ts +++ b/packages/graphql/src/schema-model/parser/annotations-parser/full-text-annotation.ts @@ -18,14 +18,13 @@ */ import type { DirectiveNode } from "graphql"; import { fulltextDirective } from "../../../graphql/directives"; -import type { FullTextField } from "../../annotation/FullTextAnnotation"; -import { FullTextAnnotation } from "../../annotation/FullTextAnnotation"; +import { FulltextAnnotation, type FulltextField } from "../../annotation/FulltextAnnotation"; import { parseArguments } from "../parse-arguments"; -export function parseFullTextAnnotation(directive: DirectiveNode): FullTextAnnotation { - const { indexes } = parseArguments<{ indexes: FullTextField[] }>(fulltextDirective, directive); +export function parseFulltextAnnotation(directive: DirectiveNode): FulltextAnnotation { + const { indexes } = parseArguments<{ indexes: FulltextField[] }>(fulltextDirective, directive); - return new FullTextAnnotation({ + return new FulltextAnnotation({ indexes, }); } diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts b/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts deleted file mode 100644 index f744105963..0000000000 --- a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { makeDirectiveNode } from "@graphql-tools/utils"; -import type { DirectiveNode } from "graphql"; -import { parseUniqueAnnotation } from "./unique-annotation"; -import { uniqueDirective } from "../../../graphql/directives"; - -describe("parseUniqueAnnotation", () => { - test("should correctly parse unique constraint name", () => { - const directive: DirectiveNode = makeDirectiveNode("unique", { constraintName: "uniqueConstraintName" }, uniqueDirective); - const uniqueAnnotation = parseUniqueAnnotation(directive); - - expect(uniqueAnnotation.constraintName).toBe("uniqueConstraintName"); - }); -}); diff --git a/packages/graphql/src/schema-model/parser/definition-collection.ts b/packages/graphql/src/schema-model/parser/definition-collection.ts index 7b5582b530..468085ca79 100644 --- a/packages/graphql/src/schema-model/parser/definition-collection.ts +++ b/packages/graphql/src/schema-model/parser/definition-collection.ts @@ -30,12 +30,14 @@ import type { UnionTypeDefinitionNode, } from "graphql"; import { Kind } from "graphql"; -import { jwt, relationshipPropertiesDirective } from "../../graphql/directives"; +import { jwt, nodeDirective, relationshipPropertiesDirective } from "../../graphql/directives"; import { isRootType } from "../../utils/is-root-type"; import { findDirective } from "./utils"; export type DefinitionCollection = { - nodes: Map; // this does not include @jwtPayload type. + nodes: Map; // includes all object types marked with @node + objectTypes: Map; // includes all objects + userDefinedObjectTypes: Map; // includes objects not reserved by the library scalarTypes: Map; enumTypes: Map; interfaceTypes: Map; @@ -43,7 +45,7 @@ export type DefinitionCollection = { directives: Map; relationshipProperties: Map; inputTypes: Map; - schemaExtension: SchemaExtensionNode | undefined; + schemaExtensions: SchemaExtensionNode | undefined; jwtPayload: ObjectTypeDefinitionNode | undefined; interfaceToImplementingTypeNamesMap: Map; // TODO: change this logic, this was the logic contained in initInterfacesToTypeNamesMap but potentially can be simplified now. operations: ObjectTypeDefinitionNode[]; @@ -58,18 +60,27 @@ export function getDefinitionCollection(document: DocumentNode): DefinitionColle case Kind.SCALAR_TYPE_DEFINITION: definitionCollection.scalarTypes.set(definition.name.value, definition); break; - case Kind.OBJECT_TYPE_DEFINITION: + case Kind.OBJECT_TYPE_DEFINITION: { + definitionCollection.objectTypes.set(definition.name.value, definition); if (findDirective(definition.directives, relationshipPropertiesDirective.name)) { definitionCollection.relationshipProperties.set(definition.name.value, definition); - } else if (findDirective(definition.directives, jwt.name)) { + break; + } + if (findDirective(definition.directives, jwt.name)) { definitionCollection.jwtPayload = definition; - } else if (!isRootType(definition)) { - definitionCollection.nodes.set(definition.name.value, definition); - } else { + break; + } + if (isRootType(definition)) { definitionCollection.operations.push(definition); + break; } - + if (findDirective(definition.directives, nodeDirective.name)) { + definitionCollection.nodes.set(definition.name.value, definition); + break; + } + definitionCollection.userDefinedObjectTypes.set(definition.name.value, definition); break; + } case Kind.ENUM_TYPE_DEFINITION: definitionCollection.enumTypes.set(definition.name.value, definition); break; @@ -88,7 +99,7 @@ export function getDefinitionCollection(document: DocumentNode): DefinitionColle break; case Kind.SCHEMA_EXTENSION: // This is based on the assumption that mergeTypeDefs is used and therefore there is only one schema extension (merged), this assumption is currently used as well for object extensions. - definitionCollection.schemaExtension = definition; + definitionCollection.schemaExtensions = definition; definitionCollection.schemaDirectives = definition.directives ? Array.from(definition.directives) : []; @@ -99,6 +110,8 @@ export function getDefinitionCollection(document: DocumentNode): DefinitionColle }, { nodes: new Map(), + objectTypes: new Map(), + userDefinedObjectTypes: new Map(), enumTypes: new Map(), scalarTypes: new Map(), interfaceTypes: new Map(), @@ -106,7 +119,7 @@ export function getDefinitionCollection(document: DocumentNode): DefinitionColle unionTypes: new Map(), relationshipProperties: new Map(), inputTypes: new Map(), - schemaExtension: undefined, + schemaExtensions: undefined, jwtPayload: undefined, interfaceToImplementingTypeNamesMap: new Map(), operations: [], diff --git a/packages/graphql/src/schema-model/parser/parse-attribute.ts b/packages/graphql/src/schema-model/parser/parse-attribute.ts index 8c73b7656b..9eb4029168 100644 --- a/packages/graphql/src/schema-model/parser/parse-attribute.ts +++ b/packages/graphql/src/schema-model/parser/parse-attribute.ts @@ -146,7 +146,7 @@ export function isUserScalar(definitionCollection: DefinitionCollection, name: s } export function isObject(definitionCollection: DefinitionCollection, name: string) { - return definitionCollection.nodes.has(name); + return definitionCollection.objectTypes.has(name); } function isInput(definitionCollection: DefinitionCollection, name: string) { diff --git a/packages/graphql/src/schema-model/relationship/Relationship.ts b/packages/graphql/src/schema-model/relationship/Relationship.ts index b2144b4c49..08dabb99b3 100644 --- a/packages/graphql/src/schema-model/relationship/Relationship.ts +++ b/packages/graphql/src/schema-model/relationship/Relationship.ts @@ -26,10 +26,10 @@ import type { Attribute } from "../attribute/Attribute"; import type { Entity } from "../entity/Entity"; export type RelationshipDirection = "IN" | "OUT"; +// "DIRECTED" | "UNDIRECTED"; export type QueryDirection = keyof typeof RelationshipQueryDirectionOption; -// "DEFAULT_DIRECTED" | "DEFAULT_UNDIRECTED" | "DIRECTED_ONLY" | "UNDIRECTED_ONLY" | "DIRECTED" | "UNDIRECTED"; -export type NestedOperation = keyof typeof RelationshipNestedOperationsOption; // "CREATE" | "UPDATE" | "DELETE" | "CONNECT" | "DISCONNECT" | "CONNECT_OR_CREATE"; +export type NestedOperation = keyof typeof RelationshipNestedOperationsOption; export class Relationship { public readonly name: string; // name of the relationship field, e.g. friends diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts index ba352de775..2dd2051af6 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.test.ts @@ -18,7 +18,6 @@ */ import { SelectableAnnotation } from "../../annotation/SelectableAnnotation"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; import { Attribute } from "../../attribute/Attribute"; import { GraphQLBuiltInScalarType, ScalarType } from "../../attribute/AttributeType"; import { ConcreteEntity } from "../../entity/ConcreteEntity"; @@ -33,7 +32,7 @@ describe("RelationshipAdapter", () => { beforeAll(() => { const userId = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); @@ -47,7 +46,7 @@ describe("RelationshipAdapter", () => { const accountId = new Attribute({ name: "id", - annotations: { unique: new UniqueAnnotation({ constraintName: "User_id_unique" }) }, + annotations: {}, type: new ScalarType(GraphQLBuiltInScalarType.ID, true), args: [], }); @@ -89,7 +88,7 @@ describe("RelationshipAdapter", () => { direction: "OUT", isList: Boolean(false), attributes: [accountAlias], - queryDirection: "DEFAULT_DIRECTED", + queryDirection: "DIRECTED", nestedOperations: ["CREATE", "UPDATE", "DELETE", "CONNECT", "DISCONNECT", "CONNECT_OR_CREATE"], aggregate: false, description: "", diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts index 267d248375..80b03d1eaa 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipAdapter.ts @@ -23,7 +23,6 @@ import type { Annotations } from "../../annotation/Annotation"; import type { Argument } from "../../argument/Argument"; import type { Attribute } from "../../attribute/Attribute"; import { AttributeAdapter } from "../../attribute/model-adapters/AttributeAdapter"; -import { ListFiltersAdapter } from "../../attribute/model-adapters/ListFiltersAdapter"; import type { Entity } from "../../entity/Entity"; import type { EntityAdapter } from "../../entity/EntityAdapter"; import { ConcreteEntityAdapter } from "../../entity/model-adapters/ConcreteEntityAdapter"; @@ -35,7 +34,6 @@ import type { NestedOperation, QueryDirection, Relationship, RelationshipDirecti import { RelationshipOperations } from "./RelationshipOperations"; export class RelationshipAdapter { - private _listFiltersModel: ListFiltersAdapter | undefined; public readonly name: string; public readonly type: string; public readonly attributes: Map = new Map(); @@ -116,15 +114,6 @@ export class RelationshipAdapter { } return this._operations; } - public get listFiltersModel(): ListFiltersAdapter | undefined { - if (!this._listFiltersModel) { - if (!this.isList) { - return; - } - this._listFiltersModel = new ListFiltersAdapter(this); - } - return this._listFiltersModel; - } public get singular(): string { if (!this._singular) { @@ -156,13 +145,8 @@ export class RelationshipAdapter { * @param directed the direction asked during the query, for instance "friends(directed: true)" * @returns the direction to use in the CypherBuilder **/ - public getCypherDirection(directed?: boolean): "left" | "right" | "undirected" { - if ( - directed === false || - this.queryDirection === "UNDIRECTED_ONLY" || - this.queryDirection === "UNDIRECTED" || - (directed === undefined && this.queryDirection === "DEFAULT_UNDIRECTED") - ) { + public getCypherDirection(): "left" | "right" | "undirected" { + if (this.queryDirection === "UNDIRECTED") { return "undirected"; } return this.cypherDirectionFromRelDirection(); @@ -224,7 +208,7 @@ export class RelationshipAdapter { // The connectOrCreate field is not generated if the related type does not have a unique field (this.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE) && relationshipTarget instanceof ConcreteEntityAdapter && - relationshipTarget.uniqueFields.length > 0) + false) ); } @@ -240,11 +224,10 @@ export class RelationshipAdapter { if (!ifUnionRelationshipTargetEntity) { throw new Error("Expected member entity"); } - const onlyConnectOrCreateAndNoUniqueFields = - onlyConnectOrCreate && !ifUnionRelationshipTargetEntity.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } - const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate && !this.target.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipBaseOperations.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipBaseOperations.ts index 3621caa355..5f760916aa 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipBaseOperations.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipBaseOperations.ts @@ -17,11 +17,10 @@ * limitations under the License. */ -import type { ConcreteEntityAdapter } from "../../entity/model-adapters/ConcreteEntityAdapter"; import { upperFirst } from "../../../utils/upper-first"; -import { isUnionEntity } from "../../../translate/queryAST/utils/is-union-entity"; -import type { RelationshipDeclarationAdapter } from "./RelationshipDeclarationAdapter"; +import type { ConcreteEntityAdapter } from "../../entity/model-adapters/ConcreteEntityAdapter"; import type { RelationshipAdapter } from "./RelationshipAdapter"; +import type { RelationshipDeclarationAdapter } from "./RelationshipDeclarationAdapter"; export abstract class RelationshipBaseOperations { protected constructor(protected readonly relationship: T) {} @@ -105,24 +104,6 @@ export abstract class RelationshipBaseOperations 0) + false) ); } @@ -208,11 +196,10 @@ export class RelationshipDeclarationAdapter { if (!ifUnionRelationshipTargetEntity) { throw new Error("Expected member entity"); } - const onlyConnectOrCreateAndNoUniqueFields = - onlyConnectOrCreate && !ifUnionRelationshipTargetEntity.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } - const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate && !this.target.uniqueFields.length; + const onlyConnectOrCreateAndNoUniqueFields = onlyConnectOrCreate; return this.nestedOperations.size > 0 && !onlyConnectOrCreateAndNoUniqueFields; } } diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationOperations.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationOperations.ts index 4b07e57c9e..9a8d0447da 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationOperations.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipDeclarationOperations.ts @@ -17,8 +17,8 @@ * limitations under the License. */ -import type { RelationshipDeclarationAdapter } from "./RelationshipDeclarationAdapter"; import { RelationshipBaseOperations } from "./RelationshipBaseOperations"; +import type { RelationshipDeclarationAdapter } from "./RelationshipDeclarationAdapter"; export class RelationshipDeclarationOperations extends RelationshipBaseOperations { constructor(relationshipDeclaration: RelationshipDeclarationAdapter) { @@ -36,4 +36,12 @@ export class RelationshipDeclarationOperations extends RelationshipBaseOperation public get relationshipPropertiesFieldTypename(): string { return `${this.relationshipFieldTypename}Properties`; } + + public get relationshipFiltersTypeName(): string { + return `${this.relationship.target.name}RelationshipFilters`; + } + + public get connectionFiltersTypeName(): string { + return `${this.prefixForTypename}ConnectionFilters`; + } } diff --git a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipOperations.ts b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipOperations.ts index 649363bb83..c4d273e455 100644 --- a/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipOperations.ts +++ b/packages/graphql/src/schema-model/relationship/model-adapters/RelationshipOperations.ts @@ -54,4 +54,12 @@ export class RelationshipOperations extends RelationshipBaseOperations; directives?: string[]; }> = [ - // TODO: REMOVE ID FIELD ON 7.x - { - name: "ID", - fields: { - shortest: composeId, - longest: composeId, - }, - directives, - }, { name: "String", fields: { diff --git a/packages/graphql/src/schema/aggregations/field-aggregation-composer.ts b/packages/graphql/src/schema/aggregations/field-aggregation-composer.ts index a4ae375e54..7576fb3950 100644 --- a/packages/graphql/src/schema/aggregations/field-aggregation-composer.ts +++ b/packages/graphql/src/schema/aggregations/field-aggregation-composer.ts @@ -26,8 +26,6 @@ import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/Uni import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; -import { DEPRECATE_ID_AGGREGATION } from "../constants"; -import { shouldAddDeprecatedFields } from "../generation/utils"; import { numericalResolver } from "../resolvers/field/numerical"; import { AggregationTypesMapper } from "./aggregation-types-mapper"; @@ -97,7 +95,7 @@ export class FieldAggregationComposer { private getAggregationFields( entity: RelationshipAdapter | ConcreteEntityAdapter | InterfaceEntityAdapter, - features: Neo4jFeaturesSettings | undefined + _features: Neo4jFeaturesSettings | undefined ): ObjectTypeComposerFieldConfigMapDefinition { return entity.aggregableFields.reduce((res, field) => { const objectTypeComposer = this.aggregationTypesMapper.getAggregationType(field.getTypeName()); @@ -105,15 +103,11 @@ export class FieldAggregationComposer { if (!objectTypeComposer) { return res; } - // TODO: REMOVE ID FIELD ON 7.x + if (field.typeHelper.isID()) { - if (shouldAddDeprecatedFields(features, "idAggregations")) { - res[field.name] = { type: objectTypeComposer.NonNull, directives: [DEPRECATE_ID_AGGREGATION] }; - } return res; - } else { - res[field.name] = objectTypeComposer.NonNull; } + res[field.name] = objectTypeComposer.NonNull; return res; }, {}); diff --git a/packages/graphql/src/schema/augment/fulltext.ts b/packages/graphql/src/schema/augment/fulltext.ts index eeb0af2213..c5525fbf53 100644 --- a/packages/graphql/src/schema/augment/fulltext.ts +++ b/packages/graphql/src/schema/augment/fulltext.ts @@ -19,73 +19,57 @@ import { GraphQLInt, GraphQLNonNull, GraphQLString } from "graphql"; import type { SchemaComposer } from "graphql-compose"; -import type { Node } from "../../classes"; +import Cypher from "@neo4j/cypher-builder"; import type { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; +import type { FulltextContext, Neo4jFeaturesSettings } from "../../types"; import { - withFullTextInputType, - withFullTextResultType, - withFullTextSortInputType, - withFullTextWhereInputType, + withFulltextResultTypeConnection, + withFulltextSortInputType, + withFulltextWhereInputType, } from "../generation/fulltext-input"; import { fulltextResolver } from "../resolvers/query/fulltext"; +import { type ComplexityEstimatorHelper } from "../../classes/ComplexityEstimatorHelper"; -export function augmentFulltextSchema( - node: Node, - composer: SchemaComposer, - concreteEntityAdapter: ConcreteEntityAdapter -) { +export function augmentFulltextSchema({ + composer, + concreteEntityAdapter, + complexityEstimatorHelper, + features, +}: { + composer: SchemaComposer; + concreteEntityAdapter: ConcreteEntityAdapter; + complexityEstimatorHelper: ComplexityEstimatorHelper + features?: Neo4jFeaturesSettings; +}) { if (!concreteEntityAdapter.annotations.fulltext) { return; } - withFullTextInputType({ concreteEntityAdapter, composer }); - withFullTextWhereInputType({ composer, concreteEntityAdapter }); + withFulltextWhereInputType({ composer, concreteEntityAdapter }); - /** - * TODO [fulltext-deprecations] - * to move this over to the concreteEntityAdapter we need to check what the use of - * the queryType and scoreVariable properties are in FulltextContext - * and determine if we can remove them - */ concreteEntityAdapter.annotations.fulltext.indexes.forEach((index) => { - /** - * TODO [fulltext-deprecations] - * remove indexName assignment and undefined check once the name argument has been removed. - */ - const indexName = index.indexName || index.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } + const fulltextContext: FulltextContext = { + index, + queryType: "query", + queryName: index.queryName, + scoreVariable: new Cypher.Variable(), + }; - let queryName = concreteEntityAdapter.operations.getFullTextIndexQueryFieldName(indexName); - if (index.queryName) { - queryName = index.queryName; - } - /** - * TODO [translation-layer-compatibility] - * temporary for compatibility with translation layer - */ - const nodeIndex = node.fulltextDirective!.indexes.find((i) => { - const iName = i.indexName || i.name; - return iName === indexName; - }); - if (!nodeIndex) { - throw new Error(`Could not find index ${indexName} on node ${node.name}`); - } + const fulltextArgs = { + phrase: new GraphQLNonNull(GraphQLString), + where: concreteEntityAdapter.operations.fulltextTypeNames.where, + sort: withFulltextSortInputType({ concreteEntityAdapter, composer }).NonNull.List, + first: features?.limitRequired ? new GraphQLNonNull(GraphQLInt) : GraphQLInt, + after: GraphQLString, + }; + + complexityEstimatorHelper.registerField("Query", index.queryName); composer.Query.addFields({ - [queryName]: { - type: withFullTextResultType({ composer, concreteEntityAdapter }).NonNull.List.NonNull, - description: - "Query a full-text index. This query returns the query score, but does not allow for aggregations. Use the `fulltext` argument under other queries for this functionality.", - resolve: fulltextResolver({ node, index: nodeIndex, entityAdapter: concreteEntityAdapter }), - args: { - phrase: new GraphQLNonNull(GraphQLString), - where: concreteEntityAdapter.operations.fulltextTypeNames.where, - sort: withFullTextSortInputType({ concreteEntityAdapter, composer }).NonNull.List, - limit: GraphQLInt, - offset: GraphQLInt, - }, + [index.queryName]: { + type: withFulltextResultTypeConnection({ composer, concreteEntityAdapter }).NonNull, + resolve: fulltextResolver({ fulltextContext, entityAdapter: concreteEntityAdapter }), + args: fulltextArgs, }, }); }); diff --git a/packages/graphql/src/schema/augment/vector.ts b/packages/graphql/src/schema/augment/vector.ts index 32e7da60b3..f4a540ea7a 100644 --- a/packages/graphql/src/schema/augment/vector.ts +++ b/packages/graphql/src/schema/augment/vector.ts @@ -29,14 +29,17 @@ import { withVectorWhereInputType, } from "../generation/vector-input"; import { vectorResolver } from "../resolvers/query/vector"; +import { type ComplexityEstimatorHelper } from "../../classes/ComplexityEstimatorHelper"; export function augmentVectorSchema({ composer, concreteEntityAdapter, + complexityEstimatorHelper, features, }: { composer: SchemaComposer; concreteEntityAdapter: ConcreteEntityAdapter; + complexityEstimatorHelper: ComplexityEstimatorHelper features?: Neo4jFeaturesSettings; }) { if (!concreteEntityAdapter.annotations.vector) { @@ -57,7 +60,7 @@ export function augmentVectorSchema({ const vectorArgs = { where: concreteEntityAdapter.operations.vectorTypeNames.where, sort: withVectorSortInputType({ concreteEntityAdapter, composer }).NonNull.List, - first: GraphQLInt, + first: features?.limitRequired ? new GraphQLNonNull(GraphQLInt) : GraphQLInt, after: GraphQLString, }; @@ -67,6 +70,7 @@ export function augmentVectorSchema({ vectorArgs["vector"] = new GraphQLList(new GraphQLNonNull(GraphQLFloat)); } + complexityEstimatorHelper.registerField("Query", index.queryName); composer.Query.addFields({ [index.queryName]: { type: withVectorResultTypeConnection({ composer, concreteEntityAdapter }).NonNull, diff --git a/packages/graphql/src/schema/constants.ts b/packages/graphql/src/schema/constants.ts index 5f7d1fc932..edea29473e 100644 --- a/packages/graphql/src/schema/constants.ts +++ b/packages/graphql/src/schema/constants.ts @@ -19,59 +19,45 @@ import { DEPRECATED } from "../constants"; -export const DEPRECATE_IMPLICIT_EQUAL_FILTERS = { - name: DEPRECATED, - args: { - reason: "Please use the explicit _EQ version", - }, -}; - -export const DEPRECATE_OPTIONS_ARGUMENT = { - name: DEPRECATED, - args: { - reason: "Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.", - }, -}; - -export const DEPRECATE_DIRECTED_ARGUMENT = { - name: DEPRECATED, - args: { - reason: "The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server", - }, -}; - -export const DEPRECATE_IMPLICIT_SET = { - name: DEPRECATED, - args: { - reason: "Please use the explicit _SET field", - }, -}; - -export const DEPRECATE_CONNECT_OR_CREATE = { - name: DEPRECATED, - args: { - reason: "The connectOrCreate operation is deprecated and will be removed", - }, -}; - -export const DEPRECATE_OVERWRITE = { - name: DEPRECATED, - args: { - reason: "The overwrite argument is deprecated and will be removed", - }, -}; - - -export const DEPRECATE_ID_AGGREGATION = { - name: DEPRECATED, - args: { - reason: "aggregation of ID fields are deprecated and will be removed", - }, -}; - -export const DEPRECATE_TYPENAME_IN = { - name: DEPRECATED, - args: { - reason: "The typename_IN filter is deprecated, please use the typename filter instead", - }, -}; +// TODO: Add constant deprecations here + +export function DEPRECATE_SET_MUTATION(name: string) { + return { + name: DEPRECATED, + args: { + reason: `Please use the generic mutation '${name}: { set: ... } }' instead.`, + }, + }; +} + +export function DEPRECATE_ARRAY_MUTATIONS(name: string, operation: "push" | "pop") { + return { + name: DEPRECATED, + args: { + reason: `Please use the generic mutation '${name}: { ${operation}: ... } }' instead.`, + }, + }; +} + +export function DEPRECATE_MATH_MUTATIONS(name: string, operation: string) { + return { + name: DEPRECATED, + args: { + reason: `Please use the relevant generic mutation '${name}: { ${operation}: ... } }' instead.`, + }, + }; +} + +export function DEPRECATE_AGGREGATION_FILTERS(name: string, aggregationOperation: string, operator: string) { + let newOperator = operator.toLowerCase(); + if (newOperator === "equal") { + newOperator = "eq"; + } + + return { + name: DEPRECATED, + args: { + reason: `Please use the relevant generic filter '${name}: { ${aggregationOperation}: { ${newOperator}: ... } } }' instead.`, + }, + }; +} diff --git a/packages/graphql/src/schema/create-relationship-fields/create-connect-or-create-field.ts b/packages/graphql/src/schema/create-relationship-fields/create-connect-or-create-field.ts deleted file mode 100644 index 45504d84d8..0000000000 --- a/packages/graphql/src/schema/create-relationship-fields/create-connect-or-create-field.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { DirectiveNode } from "graphql"; -import type { InputTypeComposer, InputTypeComposerFieldConfigMapDefinition, SchemaComposer } from "graphql-compose"; -import type { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; -import { ensureNonEmptyInput } from "../ensure-non-empty-input"; -import { withCreateInputType } from "../generation/create-input"; -import { concreteEntityToCreateInputFields } from "../to-compose"; - -export function createOnCreateITC({ - schemaComposer, - relationshipAdapter, - targetEntityAdapter, - userDefinedFieldDirectives, -}: { - schemaComposer: SchemaComposer; - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - targetEntityAdapter: ConcreteEntityAdapter; - userDefinedFieldDirectives: Map; -}): InputTypeComposer { - const onCreateInput = schemaComposer.getOrCreateITC(targetEntityAdapter.operations.onCreateInputTypeName, (tc) => { - const nodeFields = concreteEntityToCreateInputFields( - targetEntityAdapter.onCreateInputFields, - userDefinedFieldDirectives - ); - tc.addFields(nodeFields); - ensureNonEmptyInput(schemaComposer, tc); - }); - - const onCreateName = - relationshipAdapter.operations.getConnectOrCreateOnCreateFieldInputTypeName(targetEntityAdapter); - return schemaComposer.getOrCreateITC(onCreateName, (tc) => { - const onCreateFields: InputTypeComposerFieldConfigMapDefinition = { - node: onCreateInput.NonNull, - }; - if (relationshipAdapter.hasCreateInputFields) { - const edgeFieldType = withCreateInputType({ - entityAdapter: relationshipAdapter, - userDefinedFieldDirectives, - composer: schemaComposer, - }); - onCreateFields["edge"] = relationshipAdapter.hasNonNullCreateInputFields - ? edgeFieldType.NonNull - : edgeFieldType; - } - tc.addFields(onCreateFields); - }); -} diff --git a/packages/graphql/src/schema/create-relationship-fields/create-relationship-fields.ts b/packages/graphql/src/schema/create-relationship-fields/create-relationship-fields.ts index 34c9b0e1c2..30fd3f5faa 100644 --- a/packages/graphql/src/schema/create-relationship-fields/create-relationship-fields.ts +++ b/packages/graphql/src/schema/create-relationship-fields/create-relationship-fields.ts @@ -29,13 +29,11 @@ import { RelationshipAdapter } from "../../schema-model/relationship/model-adapt import { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; import { FieldAggregationComposer } from "../aggregations/field-aggregation-composer"; -import { addDirectedArgument } from "../directed-argument"; import { augmentObjectOrInterfaceTypeWithConnectionField, augmentObjectOrInterfaceTypeWithRelationshipField, } from "../generation/augment-object-or-interface"; import { augmentConnectInputTypeWithConnectFieldInput } from "../generation/connect-input"; -import { withConnectOrCreateInputType } from "../generation/connect-or-create-input"; import { augmentCreateInputTypeWithRelationshipsInput, withCreateInputType, @@ -50,6 +48,7 @@ import { withSortInputType } from "../generation/sort-and-options-input"; import { augmentUpdateInputTypeWithUpdateFieldInput, withUpdateInputType } from "../generation/update-input"; import { withSourceWhereInputType, withWhereInputType } from "../generation/where-input"; import { graphqlDirectivesToCompose } from "../to-compose"; +import { type ComplexityEstimatorHelper } from "../../classes/ComplexityEstimatorHelper"; function doForRelationshipDeclaration({ relationshipDeclarationAdapter, @@ -146,7 +145,7 @@ function doForRelationshipPropertiesType({ composer, }); withSortInputType({ relationshipAdapter, userDefinedFieldDirectives, composer }); - withUpdateInputType({ entityAdapter: relationshipAdapter, userDefinedFieldDirectives, composer }); + withUpdateInputType({ entityAdapter: relationshipAdapter, userDefinedFieldDirectives, composer, features }); withWhereInputType({ entityAdapter: relationshipAdapter, userDefinedFieldDirectives, @@ -167,6 +166,7 @@ export function createRelationshipFields({ userDefinedDirectivesForNode, userDefinedFieldDirectivesForNode, features, + complexityEstimatorHelper, }: { entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter; schemaComposer: SchemaComposer; @@ -177,6 +177,7 @@ export function createRelationshipFields({ userDefinedDirectivesForNode: Map; userDefinedFieldDirectivesForNode: Map>; features?: Neo4jFeaturesSettings; + complexityEstimatorHelper: ComplexityEstimatorHelper; }): void { const relationships = entityAdapter instanceof ConcreteEntityAdapter @@ -192,6 +193,12 @@ export function createRelationshipFields({ return; } + if (!relationshipAdapter.isList) { + throw new Error( + `@relationship on non-list field [${relationshipAdapter.source.name}.${relationshipAdapter.name}] not supported` + ); + } + // TODO: find a way to merge these 2 into 1 RelationshipProperties generation function if (relationshipAdapter instanceof RelationshipDeclarationAdapter) { doForRelationshipDeclaration({ @@ -239,6 +246,7 @@ export function createRelationshipFields({ userDefinedDirectivesOnTargetFields: Map | undefined; subgraph?: Subgraph; features: Neo4jFeaturesSettings | undefined; + complexityEstimatorHelper: ComplexityEstimatorHelper; } = { relationshipAdapter, composer: schemaComposer, @@ -247,6 +255,7 @@ export function createRelationshipFields({ deprecatedDirectives, userDefinedDirectivesOnTargetFields, features, + complexityEstimatorHelper, }; if (relationshipTarget instanceof UnionEntityAdapter) { @@ -268,13 +277,11 @@ export function createRelationshipFields({ where: relationshipTarget.operations.whereInputTypeName, }; - const aggregationFieldsArgs = addDirectedArgument(aggregationFieldsBaseArgs, relationshipAdapter, features); - if (relationshipAdapter.aggregate) { composeNode.addFields({ [relationshipAdapter.operations.aggregateTypeName]: { type: aggregationTypeObject, - args: aggregationFieldsArgs, + args: aggregationFieldsBaseArgs, directives: deprecatedDirectives, }, }); @@ -298,6 +305,7 @@ function createRelationshipFieldsForTarget({ userDefinedDirectivesOnTargetFields, subgraph, // only for concrete targets features, + complexityEstimatorHelper, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; @@ -307,6 +315,7 @@ function createRelationshipFieldsForTarget({ deprecatedDirectives: Directive[]; subgraph?: Subgraph; features: Neo4jFeaturesSettings | undefined; + complexityEstimatorHelper: ComplexityEstimatorHelper; }) { withSourceWhereInputType({ relationshipAdapter, @@ -317,33 +326,23 @@ function createRelationshipFieldsForTarget({ }); if (relationshipAdapter.target instanceof InterfaceEntityAdapter) { - withFieldInputType({ relationshipAdapter, composer, userDefinedFieldDirectives, features }); - } else { - withConnectOrCreateInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - deprecatedDirectives, - }); + withFieldInputType({ relationshipAdapter, composer, userDefinedFieldDirectives }); } + complexityEstimatorHelper.registerField(composeNode.getTypeName(), relationshipAdapter.name) composeNode.addFields( augmentObjectOrInterfaceTypeWithRelationshipField({ relationshipAdapter, userDefinedFieldDirectives, subgraph, - features, composer, + features, }) ); - + + complexityEstimatorHelper.registerField(composeNode.getTypeName(), relationshipAdapter.operations.connectionFieldName) composeNode.addFields( - augmentObjectOrInterfaceTypeWithConnectionField( - relationshipAdapter, - userDefinedFieldDirectives, - composer, - features - ) + augmentObjectOrInterfaceTypeWithConnectionField(relationshipAdapter, userDefinedFieldDirectives, composer, features) ); withRelationInputType({ diff --git a/packages/graphql/src/schema/create-relationship-fields/fields/overwrite.ts b/packages/graphql/src/schema/create-relationship-fields/fields/overwrite.ts deleted file mode 100644 index 93bfd3ec32..0000000000 --- a/packages/graphql/src/schema/create-relationship-fields/fields/overwrite.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GraphQLBoolean, GraphQLNonNull } from "graphql"; -import type { InputTypeComposerFieldConfigDefinition } from "graphql-compose"; -import { DEPRECATE_OVERWRITE } from "../../constants"; - -export const overwrite: InputTypeComposerFieldConfigDefinition = { - type: new GraphQLNonNull(GraphQLBoolean), - description: "Whether or not to overwrite any matching relationship with the new properties.", - defaultValue: true, - directives: [DEPRECATE_OVERWRITE], -}; diff --git a/packages/graphql/src/schema/directed-argument.test.ts b/packages/graphql/src/schema/directed-argument.test.ts deleted file mode 100644 index 6f43f513bc..0000000000 --- a/packages/graphql/src/schema/directed-argument.test.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { RelationshipQueryDirectionOption } from "../constants"; -import type { RelationshipAdapter } from "../schema-model/relationship/model-adapters/RelationshipAdapter"; -import { DEPRECATE_DIRECTED_ARGUMENT } from "./constants"; -import { addDirectedArgument, getDirectedArgument } from "./directed-argument"; - -describe("Directed argument", () => { - describe("getDirectedArgument", () => { - test("should return default true argument for DEFAULT_DIRECTED (deprecated)", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.DEFAULT_DIRECTED, - } as RelationshipAdapter, - {} - ) - ).toEqual({ - type: "Boolean", - defaultValue: true, - directives: [DEPRECATE_DIRECTED_ARGUMENT], - }); - }); - - test("should return default false argument for DEFAULT_UNDIRECTED (deprecated)", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.DEFAULT_UNDIRECTED, - } as RelationshipAdapter, - {} - ) - ).toEqual({ - type: "Boolean", - defaultValue: false, - directives: [DEPRECATE_DIRECTED_ARGUMENT], - }); - }); - - test("should return an undefined argument for DIRECTED_ONLY (deprecated)", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.DIRECTED_ONLY, - } as RelationshipAdapter, - {} - ) - ).toBeUndefined(); - }); - - test("should return an undefined argument for UNDIRECTED_ONLY (deprecated)", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.UNDIRECTED_ONLY, - } as RelationshipAdapter, - {} - ) - ).toBeUndefined(); - }); - - test("should return an undefined argument for DIRECTED", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.DIRECTED, - } as RelationshipAdapter, - {} - ) - ).toBeUndefined(); - }); - - test("should return an undefined argument for UNDIRECTED", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.UNDIRECTED, - } as RelationshipAdapter, - {} - ) - ).toBeUndefined(); - }); - - test("should return undefined if directedArgument in excludeDeprecatedDirectives", () => { - expect( - getDirectedArgument( - { - queryDirection: RelationshipQueryDirectionOption.DEFAULT_UNDIRECTED, - } as RelationshipAdapter, - { excludeDeprecatedFields: { directedArgument: true } } - ) - ).toBeUndefined(); - }); - }); - - describe("addDirectedArgument", () => { - test("should add directed argument if DEFAULT_DIRECTED", () => { - const args = addDirectedArgument( - { arg1: "dsa" }, - { - queryDirection: RelationshipQueryDirectionOption.DEFAULT_DIRECTED, - } as RelationshipAdapter, - {} - ); - expect(args).toEqual({ - arg1: "dsa", - directed: { - type: "Boolean", - defaultValue: true, - directives: [DEPRECATE_DIRECTED_ARGUMENT], - }, - }); - }); - test("should not add any argument if DIRECTED_ONLY", () => { - const args = addDirectedArgument( - { arg1: "dsa" }, - { - queryDirection: RelationshipQueryDirectionOption.DIRECTED_ONLY, - } as RelationshipAdapter, - {} - ); - expect(args).toEqual({ - arg1: "dsa", - }); - }); - - test("should not add any argument if DIRECTED", () => { - const args = addDirectedArgument( - { arg1: "dsa" }, - { - queryDirection: RelationshipQueryDirectionOption.DIRECTED, - } as RelationshipAdapter, - {} - ); - expect(args).toEqual({ - arg1: "dsa", - }); - }); - }); -}); diff --git a/packages/graphql/src/schema/directed-argument.ts b/packages/graphql/src/schema/directed-argument.ts deleted file mode 100644 index b0bbfe8473..0000000000 --- a/packages/graphql/src/schema/directed-argument.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Directive } from "graphql-compose"; -import { RelationshipQueryDirectionOption } from "../constants"; -import type { RelationshipAdapter } from "../schema-model/relationship/model-adapters/RelationshipAdapter"; -import { RelationshipDeclarationAdapter } from "../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; -import type { Neo4jFeaturesSettings } from "../types"; -import { DEPRECATE_DIRECTED_ARGUMENT } from "./constants"; -import { shouldAddDeprecatedFields } from "./generation/utils"; - -type DirectedArgument = { - type: "Boolean"; - defaultValue: boolean; - directives: Directive[]; -}; - -export function getDirectedArgument( - relationshipAdapter: RelationshipAdapter, - features: Neo4jFeaturesSettings | undefined -): DirectedArgument | undefined { - let defaultValue: boolean; - switch (relationshipAdapter.queryDirection) { - case RelationshipQueryDirectionOption.DEFAULT_DIRECTED: - defaultValue = true; - break; - case RelationshipQueryDirectionOption.DEFAULT_UNDIRECTED: - defaultValue = false; - break; - default: - return undefined; - } - - if (shouldAddDeprecatedFields(features, "directedArgument")) { - return { - type: "Boolean", - defaultValue, - directives: [DEPRECATE_DIRECTED_ARGUMENT], - }; - } -} - -export function addDirectedArgument>( - args: T, - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter, - features: Neo4jFeaturesSettings | undefined -): T & { directed?: DirectedArgument } { - if (relationshipAdapter instanceof RelationshipDeclarationAdapter) { - return { ...args }; - } - - const directedArg = getDirectedArgument(relationshipAdapter, features); - if (directedArg) { - return { ...args, directed: directedArg }; - } - - return { ...args }; -} diff --git a/packages/graphql/src/schema/generation/AugmentedSchemaGenerator.ts b/packages/graphql/src/schema/generation/AugmentedSchemaGenerator.ts index 041e99de00..9f1b1bc6e7 100644 --- a/packages/graphql/src/schema/generation/AugmentedSchemaGenerator.ts +++ b/packages/graphql/src/schema/generation/AugmentedSchemaGenerator.ts @@ -25,7 +25,6 @@ import type { GraphQLScalarType, ObjectTypeDefinitionNode, } from "graphql"; -import { Kind, print } from "graphql"; import { SchemaComposer } from "graphql-compose"; import { SortDirection } from "../../graphql/enums/SortDirection"; import { CartesianPointDistance } from "../../graphql/input-objects/CartesianPointDistance"; @@ -43,16 +42,16 @@ import { UpdateInfo } from "../../graphql/objects/UpdateInfo"; import * as Scalars from "../../graphql/scalars"; import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel"; import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import { InterfaceEntityAdapter } from "../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; +import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; +import type { DefinitionCollection } from "../../schema-model/parser/definition-collection"; import { getEntityAdapter } from "../../schema-model/utils/get-entity-adapter"; -import type { DefinitionNodes } from "../get-definition-nodes"; export class AugmentedSchemaGenerator { private composer: SchemaComposer; constructor( private schemaModel: Neo4jGraphQLSchemaModel, - private definitionNodes: DefinitionNodes, + private definitionNodes: DefinitionCollection, private rootTypesCustomResolvers: ObjectTypeDefinitionNode[] ) { this.composer = new SchemaComposer(); @@ -71,28 +70,28 @@ export class AugmentedSchemaGenerator { for (const entity of this.schemaModel.entities.values()) { const model = getEntityAdapter(entity); - // TODO: check if these can be created ad-hoc - if (model instanceof ConcreteEntityAdapter || model instanceof InterfaceEntityAdapter) { - for (const attribute of model.attributes.values()) { - if (attribute.typeHelper.isPoint()) { - pointInTypeDefs = true; - } - if (attribute.typeHelper.isCartesianPoint()) { - cartesianPointInTypeDefs = true; - } + if (model instanceof UnionEntityAdapter) { + continue; + } + for (const attribute of model.attributes.values()) { + if (attribute.typeHelper.isPoint()) { + pointInTypeDefs = true; } - if (model.annotations.fulltext || model.annotations.vector) { - floatWhereInTypeDefs = true; + if (attribute.typeHelper.isCartesianPoint()) { + cartesianPointInTypeDefs = true; } - if (model instanceof ConcreteEntityAdapter) { - for (const relationship of model.relationships.values()) { - for (const attribute of relationship.attributes.values()) { - if (attribute.typeHelper.isPoint()) { - pointInTypeDefs = true; - } - if (attribute.typeHelper.isCartesianPoint()) { - cartesianPointInTypeDefs = true; - } + } + if (model.annotations.fulltext || model.annotations.vector) { + floatWhereInTypeDefs = true; + } + if (model instanceof ConcreteEntityAdapter) { + for (const relationship of model.relationships.values()) { + for (const attribute of relationship.attributes.values()) { + if (attribute.typeHelper.isPoint()) { + pointInTypeDefs = true; + } + if (attribute.typeHelper.isCartesianPoint()) { + cartesianPointInTypeDefs = true; } } } @@ -107,20 +106,6 @@ export class AugmentedSchemaGenerator { return this.composer; } - private pipeDefs() { - const pipedDefs = [ - ...this.definitionNodes.enumTypes, - ...this.definitionNodes.scalarTypes, - ...this.definitionNodes.inputObjectTypes, - ...this.definitionNodes.unionTypes, - ...this.definitionNodes.directives, - ...this.rootTypesCustomResolvers, - ].filter(Boolean); - if (pipedDefs.length) { - this.composer.addTypeDefs(print({ kind: Kind.DOCUMENT, definitions: pipedDefs })); - } - } - private getStaticTypes() { return { objects: [CreateInfo, DeleteInfo, UpdateInfo, PageInfo], diff --git a/packages/graphql/src/schema/generation/aggregate-types.ts b/packages/graphql/src/schema/generation/aggregate-types.ts index 6240c8230a..40bc4a4310 100644 --- a/packages/graphql/src/schema/generation/aggregate-types.ts +++ b/packages/graphql/src/schema/generation/aggregate-types.ts @@ -25,7 +25,8 @@ import type { ObjectTypeComposerFieldConfigMapDefinition, SchemaComposer, } from "graphql-compose"; -import { AGGREGATION_COMPARISON_OPERATORS, DEPRECATED } from "../../constants"; +import { AGGREGATION_COMPARISON_OPERATORS } from "../../constants"; +import { IntScalarFilters } from "../../graphql/input-objects/generic-operators/IntScalarFilters"; import type { AttributeAdapter } from "../../schema-model/attribute/model-adapters/AttributeAdapter"; import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { InterfaceEntityAdapter } from "../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; @@ -33,9 +34,10 @@ import type { RelationshipAdapter } from "../../schema-model/relationship/model- import { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; import type { AggregationTypesMapper } from "../aggregations/aggregation-types-mapper"; -import { DEPRECATE_ID_AGGREGATION, DEPRECATE_IMPLICIT_EQUAL_FILTERS } from "../constants"; +import { DEPRECATE_AGGREGATION_FILTERS } from "../constants"; import { numericalResolver } from "../resolvers/field/numerical"; import { graphqlDirectivesToCompose } from "../to-compose"; +import { getAggregationFilterFromAttributeType } from "./get-aggregation-filter-from-attribute-type"; import { shouldAddDeprecatedFields } from "./utils"; export function withAggregateSelectionType({ @@ -80,16 +82,6 @@ function makeAggregableFields({ for (const attribute of aggregableAttributes) { const objectTypeComposer = aggregationTypesMapper.getAggregationType(attribute.getTypeName()); if (objectTypeComposer) { - // TODO: REMOVE ID FIELD ON 7.x - if (attribute.typeHelper.isID()) { - if (shouldAddDeprecatedFields(features, "idAggregations")) { - aggregableFields[attribute.name] = { - type: objectTypeComposer.NonNull, - directives: [DEPRECATE_ID_AGGREGATION], - }; - } - continue; - } aggregableFields[attribute.name] = { type: objectTypeComposer.NonNull }; } } @@ -122,15 +114,10 @@ export function withAggregateInputType({ count_LTE: GraphQLInt, count_GT: GraphQLInt, count_GTE: GraphQLInt, + count: IntScalarFilters, }, }); - if (shouldAddDeprecatedFields(features, "implicitEqualFilters")) { - aggregateWhereInput.addFields({ - count: { type: GraphQLInt, directives: [DEPRECATE_IMPLICIT_EQUAL_FILTERS] }, - }); - } - aggregateWhereInput.addFields({ AND: aggregateWhereInput.NonNull.List, OR: aggregateWhereInput.NonNull.List, @@ -213,42 +200,42 @@ function makeAggregationFields( ): InputTypeComposerFieldConfigMapDefinition { const fields: InputTypeComposerFieldConfigMapDefinition = {}; for (const attribute of attributes) { - addAggregationFieldsByType( - attribute, - userDefinedDirectivesOnTargetFields?.get(attribute.name), - fields, - features - ); + if (shouldAddDeprecatedFields(features, "aggregationFilters")) { + addDeprecatedAggregationFieldsByType( + attribute, + userDefinedDirectivesOnTargetFields?.get(attribute.name), + fields + ); + } + if (attribute.isAggregationWhereField()) { + fields[attribute.name] = getAggregationFilterFromAttributeType(attribute); + } } return fields; } // TODO: refactor this by introducing specialized Adapters -function addAggregationFieldsByType( +function addDeprecatedAggregationFieldsByType( attribute: AttributeAdapter, directivesOnField: DirectiveNode[] | undefined, fields: InputTypeComposerFieldConfigMapDefinition, - features: Neo4jFeaturesSettings | undefined ): InputTypeComposerFieldConfigMapDefinition { - const deprecatedDirectives = graphqlDirectivesToCompose( - (directivesOnField || []).filter((d) => d.name.value === DEPRECATED) - ); - if (attribute.typeHelper.isString()) { for (const operator of AGGREGATION_COMPARISON_OPERATORS) { fields[`${attribute.name}_AVERAGE_LENGTH_${operator}`] = { type: GraphQLFloat, - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "averageLength", operator)], }; fields[`${attribute.name}_LONGEST_LENGTH_${operator}`] = { type: GraphQLInt, - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "longestLength", operator)], }; fields[`${attribute.name}_SHORTEST_LENGTH_${operator}`] = { type: GraphQLInt, - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "shortestLength", operator)], }; } + return fields; } if (attribute.typeHelper.isNumeric() || attribute.typeHelper.isDuration()) { @@ -259,16 +246,16 @@ function addAggregationFieldsByType( for (const operator of AGGREGATION_COMPARISON_OPERATORS) { fields[`${attribute.name}_MIN_${operator}`] = { type: attribute.getTypeName(), - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "min", operator)], }; fields[`${attribute.name}_MAX_${operator}`] = { type: attribute.getTypeName(), - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "max", operator)], }; if (attribute.getTypeName() !== "Duration") { fields[`${attribute.name}_SUM_${operator}`] = { type: attribute.getTypeName(), - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "sum", operator)], }; } const averageType = attribute.typeHelper.isBigInt() @@ -276,34 +263,22 @@ function addAggregationFieldsByType( : attribute.typeHelper.isDuration() ? "Duration" : GraphQLFloat; - fields[`${attribute.name}_AVERAGE_${operator}`] = { type: averageType, directives: deprecatedDirectives }; + fields[`${attribute.name}_AVERAGE_${operator}`] = { + type: averageType, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "average", operator)], + }; } + return fields; } for (const operator of AGGREGATION_COMPARISON_OPERATORS) { - // TODO: REMOVE ID FIELD ON 7.x - if (attribute.typeHelper.isID()) { - if (shouldAddDeprecatedFields(features, "idAggregations")) { - fields[`${attribute.name}_MIN_${operator}`] = { - type: attribute.getTypeName(), - directives: deprecatedDirectives.length ? deprecatedDirectives : [DEPRECATE_ID_AGGREGATION], - }; - fields[`${attribute.name}_MAX_${operator}`] = { - type: attribute.getTypeName(), - directives: deprecatedDirectives.length ? deprecatedDirectives : [DEPRECATE_ID_AGGREGATION], - }; - } - - continue; - } - fields[`${attribute.name}_MIN_${operator}`] = { type: attribute.getTypeName(), - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "min", operator)], }; fields[`${attribute.name}_MAX_${operator}`] = { type: attribute.getTypeName(), - directives: deprecatedDirectives, + directives: [DEPRECATE_AGGREGATION_FILTERS(attribute.name, "max", operator)], }; } return fields; diff --git a/packages/graphql/src/schema/generation/augment-object-or-interface.ts b/packages/graphql/src/schema/generation/augment-object-or-interface.ts index 4b7fec3cb7..a174c9240b 100644 --- a/packages/graphql/src/schema/generation/augment-object-or-interface.ts +++ b/packages/graphql/src/schema/generation/augment-object-or-interface.ts @@ -16,18 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { GraphQLInt, GraphQLString, type DirectiveNode, type GraphQLResolveInfo } from "graphql"; +import { GraphQLInt, GraphQLNonNull, GraphQLString, type DirectiveNode, type GraphQLResolveInfo } from "graphql"; import type { Directive, ObjectTypeComposerArgumentConfigMapDefinition, SchemaComposer } from "graphql-compose"; import type { Subgraph } from "../../classes/Subgraph"; import { DEPRECATED } from "../../constants"; -import { QueryOptions } from "../../graphql/input-objects/QueryOptions"; import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { ConnectionQueryArgs, Neo4jFeaturesSettings } from "../../types"; -import { DEPRECATE_OPTIONS_ARGUMENT } from "../constants"; -import { addDirectedArgument, getDirectedArgument } from "../directed-argument"; import { connectionFieldResolver } from "../pagination"; import { graphqlDirectivesToCompose } from "../to-compose"; import { @@ -36,20 +33,19 @@ import { withConnectionSortInputType, } from "./connection-where-input"; import { makeSortInput } from "./sort-and-options-input"; -import { shouldAddDeprecatedFields } from "./utils"; export function augmentObjectOrInterfaceTypeWithRelationshipField({ relationshipAdapter, userDefinedFieldDirectives, subgraph, - features, composer, + features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; userDefinedFieldDirectives: Map; subgraph?: Subgraph | undefined; - features?: Neo4jFeaturesSettings; composer: SchemaComposer; + features: Neo4jFeaturesSettings | undefined; }): Record { const fields = {}; const relationshipField: { type: string; description?: string; directives: Directive[]; args?: any } = { @@ -73,16 +69,12 @@ export function augmentObjectOrInterfaceTypeWithRelationshipField({ ? relationshipAdapter.originalTarget : relationshipAdapter.target; - const optionsTypeName = - relationshipTarget instanceof UnionEntityAdapter - ? QueryOptions - : relationshipTarget.operations.optionsInputTypeName; const whereTypeName = relationshipTarget.operations.whereInputTypeName; const nodeFieldsArgs = { where: whereTypeName, - limit: "Int", - offset: "Int", + limit: features?.limitRequired ? new GraphQLNonNull(GraphQLInt) : GraphQLInt, + offset: GraphQLInt, }; if (!(relationshipTarget instanceof UnionEntityAdapter)) { const sortConfig = makeSortInput({ @@ -94,20 +86,7 @@ export function augmentObjectOrInterfaceTypeWithRelationshipField({ nodeFieldsArgs["sort"] = sortConfig.NonNull.List; } } - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - if (shouldAddDeprecatedFields(features, "deprecatedOptionsArgument")) { - nodeFieldsArgs["options"] = { - type: optionsTypeName, - directives: [DEPRECATE_OPTIONS_ARGUMENT], - }; - } - if (relationshipAdapter instanceof RelationshipAdapter) { - const directedArg = getDirectedArgument(relationshipAdapter, features); - if (directedArg) { - nodeFieldsArgs["directed"] = directedArg; - } - } relationshipField.args = nodeFieldsArgs; } @@ -129,22 +108,18 @@ export function augmentObjectOrInterfaceTypeWithConnectionField( (directive) => directive.name.value === DEPRECATED ) ); - const composeNodeArgs = addDirectedArgument( - { - where: makeConnectionWhereInputType({ - relationshipAdapter, - composer: schemaComposer, - }), - first: { - type: GraphQLInt, - }, - after: { - type: GraphQLString, - }, + const composeNodeArgs: ObjectTypeComposerArgumentConfigMapDefinition = { + where: makeConnectionWhereInputType({ + relationshipAdapter, + composer: schemaComposer, + }), + first: { + type: features?.limitRequired ? new GraphQLNonNull(GraphQLInt): GraphQLInt, }, - relationshipAdapter, - features - ); + after: { + type: GraphQLString, + }, + }; const connectionSortITC = withConnectionSortInputType({ relationshipAdapter, composer: schemaComposer, diff --git a/packages/graphql/src/schema/generation/augment-where-input.ts b/packages/graphql/src/schema/generation/augment-where-input.ts index 50466d051f..5bf22e4a7e 100644 --- a/packages/graphql/src/schema/generation/augment-where-input.ts +++ b/packages/graphql/src/schema/generation/augment-where-input.ts @@ -17,77 +17,284 @@ * limitations under the License. */ -import type { Directive, InputTypeComposerFieldConfigMapDefinition } from "graphql-compose"; +import type { + Directive, + InputTypeComposer, + InputTypeComposerFieldConfigMapDefinition, + SchemaComposer, +} from "graphql-compose"; +import pluralize from "pluralize"; +import { DEPRECATED } from "../../constants"; import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; +import type { Neo4jFeaturesSettings } from "../../types"; +import { shouldAddDeprecatedFields } from "./utils"; -function augmentRelationshipWhereInputType({ - whereType, - fieldName, - filters, +type FieldConfig = { + name: string; + typeName: string; + description: string; + deprecationReason?: string; +}; + +export function augmentWhereInputWithRelationshipFilters({ + whereInput, + composer, relationshipAdapter, deprecatedDirectives, + features, }: { - whereType: string; - fieldName: string; - filters: - | { - type: string; - description: string; - }[] - | undefined; + whereInput: InputTypeComposer; + composer: SchemaComposer; relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; deprecatedDirectives: Directive[]; -}): InputTypeComposerFieldConfigMapDefinition { - const fields: InputTypeComposerFieldConfigMapDefinition = {}; + features?: Neo4jFeaturesSettings; +}) { if (!relationshipAdapter.isFilterableByValue()) { - return fields; + return {}; } - if (!relationshipAdapter.isList) { - fields[fieldName] = { - type: whereType, - }; - } + // Relationship filters + const relationshipFiltersFields = fieldConfigsToFieldConfigMap({ + deprecatedDirectives, + fields: getRelationshipFilters({ relationshipAdapter }), + }); - if (filters) { - for (const filterField of filters) { - fields[filterField.type] = { - type: whereType, - directives: deprecatedDirectives, - // e.g. "Return Movies where all of the related Actors match this filter" - description: filterField.description, - }; - } - } + composer.getOrCreateITC(relationshipAdapter.operations.relationshipFiltersTypeName, (itc) => { + itc.addFields(relationshipFiltersFields); + }); - return fields; -} + whereInput.addFields({ + [relationshipAdapter.name]: { + type: relationshipAdapter.operations.relationshipFiltersTypeName, + }, + }); -export function augmentWhereInputTypeWithRelationshipFields( - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter, - deprecatedDirectives: Directive[] -): InputTypeComposerFieldConfigMapDefinition { - const filters = relationshipAdapter.listFiltersModel?.filters; - return augmentRelationshipWhereInputType({ - whereType: relationshipAdapter.target.operations.whereInputTypeName, - fieldName: relationshipAdapter.name, - filters, - relationshipAdapter, + // Connection filters + const connectionFiltersFields = fieldConfigsToFieldConfigMap({ deprecatedDirectives, + fields: getRelationshipConnectionFilters(relationshipAdapter), }); -} -export function augmentWhereInputTypeWithConnectionFields( - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter, - deprecatedDirectives: Directive[] -): InputTypeComposerFieldConfigMapDefinition { - const filters = relationshipAdapter.listFiltersModel?.connectionFilters; - return augmentRelationshipWhereInputType({ - whereType: relationshipAdapter.operations.getConnectionWhereTypename(), - fieldName: relationshipAdapter.operations.connectionFieldName, - filters, - relationshipAdapter, - deprecatedDirectives, + composer.getOrCreateITC(relationshipAdapter.operations.connectionFiltersTypeName, (itc) => { + itc.addFields(connectionFiltersFields); + }); + + whereInput.addFields({ + [relationshipAdapter.operations.connectionFieldName]: { + type: relationshipAdapter.operations.connectionFiltersTypeName, + }, }); + if (shouldAddDeprecatedFields(features, "relationshipFilters")) { + // Add relationship legacy filter fields + const legacyRelationship = fieldConfigsToFieldConfigMap({ + deprecatedDirectives, + fields: getRelationshipFiltersLegacy(relationshipAdapter), + }); + whereInput.addFields(legacyRelationship); + + // Add connection legacy filter fields + const legacyConnection = fieldConfigsToFieldConfigMap({ + deprecatedDirectives, + fields: getRelationshipConnectionFiltersLegacy(relationshipAdapter), + }); + whereInput.addFields(legacyConnection); + } +} +// exported as reused by Cypher filters +export function getRelationshipFilters({ + relationshipInfo, + relationshipAdapter, +}: { + relationshipInfo?: { targetName: string; inputTypeName: string }; + relationshipAdapter?: RelationshipAdapter | RelationshipDeclarationAdapter; +}): FieldConfig[] { + if (relationshipAdapter) { + return getRelationshipFiltersUsingOptions({ + targetName: relationshipAdapter.target.name, + inputTypeName: relationshipAdapter.target.operations.whereInputTypeName, + }); + } else if (relationshipInfo) { + return getRelationshipFiltersUsingOptions(relationshipInfo); + } + throw new Error("Either relationshipInfo or relationshipAdapter must be provided to getRelationshipFilters method"); +} + +function getRelationshipFiltersUsingOptions({ + targetName, + inputTypeName, +}: { + targetName: string; + inputTypeName: string; +}): FieldConfig[] { + return [ + { + name: "all", + typeName: inputTypeName, + description: `Filter type where all of the related ${pluralize(targetName)} match this filter`, + }, + + { + name: "none", + typeName: inputTypeName, + description: `Filter type where none of the related ${pluralize(targetName)} match this filter`, + }, + + { + name: "single", + typeName: inputTypeName, + description: `Filter type where one of the related ${pluralize(targetName)} match this filter`, + }, + + { + name: "some", + typeName: inputTypeName, + description: `Filter type where some of the related ${pluralize(targetName)} match this filter`, + }, + ]; +} + +function getRelationshipConnectionFilters( + relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter +): FieldConfig[] { + return [ + { + name: "all", + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where all of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + }, + { + name: "none", + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where none of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + }, + { + name: "single", + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where one of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + }, + { + name: "some", + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where some of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + }, + ]; +} + +function getRelationshipFiltersLegacy( + relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter +): FieldConfig[] { + return [ + { + name: `${relationshipAdapter.name}_ALL`, + typeName: relationshipAdapter.target.operations.whereInputTypeName, + description: `Return ${pluralize( + relationshipAdapter.source.name + )} where all of the related ${pluralize(relationshipAdapter.target.name)} match this filter`, + + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.name}: { all: ... }' instead.`, + }, + { + name: `${relationshipAdapter.name}_NONE`, + typeName: relationshipAdapter.target.operations.whereInputTypeName, + description: `Return ${pluralize( + relationshipAdapter.source.name + )} where none of the related ${pluralize(relationshipAdapter.target.name)} match this filter`, + + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.name}: { none: ... }' instead.`, + }, + { + name: `${relationshipAdapter.name}_SINGLE`, + typeName: relationshipAdapter.target.operations.whereInputTypeName, + description: `Return ${pluralize( + relationshipAdapter.source.name + )} where one of the related ${pluralize(relationshipAdapter.target.name)} match this filter`, + + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.name}: { single: ... }' instead.`, + }, + { + name: `${relationshipAdapter.name}_SOME`, + typeName: relationshipAdapter.target.operations.whereInputTypeName, + description: `Return ${pluralize( + relationshipAdapter.source.name + )} where some of the related ${pluralize(relationshipAdapter.target.name)} match this filter`, + + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.name}: { some: ... }' instead.`, + }, + ]; +} + +function getRelationshipConnectionFiltersLegacy( + relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter +): FieldConfig[] { + return [ + { + name: `${relationshipAdapter.operations.connectionFieldName}_ALL`, + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where all of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.operations.connectionFieldName}: { all: { node: ... } } }' instead.`, + }, + { + name: `${relationshipAdapter.operations.connectionFieldName}_NONE`, + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where none of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.operations.connectionFieldName}: { none: { node: ... } } }' instead.`, + }, + { + name: `${relationshipAdapter.operations.connectionFieldName}_SINGLE`, + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where one of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.operations.connectionFieldName}: { single: { node: ... } } }' instead.`, + }, + { + name: `${relationshipAdapter.operations.connectionFieldName}_SOME`, + typeName: relationshipAdapter.operations.getConnectionWhereTypename(), + description: `Return ${pluralize(relationshipAdapter.source.name)} where some of the related ${pluralize( + relationshipAdapter.operations.connectionFieldTypename + )} match this filter`, + deprecationReason: `Please use the relevant generic filter '${relationshipAdapter.operations.connectionFieldName}: { some: { node: ... } } }' instead.`, + }, + ]; +} + +// NOTE: This used to be a specialized function used specifically to generate relationship fields, +// but now after this refactor, it could be used as schema composer utility if needed. +export function fieldConfigsToFieldConfigMap({ + deprecatedDirectives, + fields, +}: { + deprecatedDirectives: Directive[]; + fields: FieldConfig[]; +}): InputTypeComposerFieldConfigMapDefinition { + const fieldsConfigMap: InputTypeComposerFieldConfigMapDefinition = {}; + + for (const field of fields) { + let directives; + if (deprecatedDirectives.length) { + directives = deprecatedDirectives; + } else if (field.deprecationReason) { + directives = [{ name: DEPRECATED, args: { reason: field.deprecationReason } }]; + } + fieldsConfigMap[field.name] = { + type: field.typeName, + directives, + description: field.description, + }; + } + + return fieldsConfigMap; } diff --git a/packages/graphql/src/schema/generation/connect-input.ts b/packages/graphql/src/schema/generation/connect-input.ts index 689b401182..e211bd9d2e 100644 --- a/packages/graphql/src/schema/generation/connect-input.ts +++ b/packages/graphql/src/schema/generation/connect-input.ts @@ -30,7 +30,6 @@ import { InterfaceEntityAdapter } from "../../schema-model/entity/model-adapters import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; -import { overwrite } from "../create-relationship-fields/fields/overwrite"; import { relationshipTargetHasRelationshipWithNestedOperation } from "./utils"; import { withConnectWhereFieldInputType } from "./where-input"; @@ -216,7 +215,6 @@ function makeConnectFieldInputTypeFields({ } if (relationshipAdapter.target instanceof ConcreteEntityAdapter) { fields["where"] = withConnectWhereFieldInputType(relationshipAdapter.target, composer); - fields["overwrite"] = overwrite; if ( relationshipTargetHasRelationshipWithNestedOperation( relationshipAdapter.target, diff --git a/packages/graphql/src/schema/generation/connect-or-create-input.ts b/packages/graphql/src/schema/generation/connect-or-create-input.ts deleted file mode 100644 index 729a8603ec..0000000000 --- a/packages/graphql/src/schema/generation/connect-or-create-input.ts +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import type { DirectiveNode } from "graphql"; -import type { - Directive, - InputTypeComposer, - InputTypeComposerFieldConfigMap, - InputTypeComposerFieldConfigMapDefinition, - SchemaComposer, -} from "graphql-compose"; -import { RelationshipNestedOperationsOption } from "../../constants"; -import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; -import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; -import { createOnCreateITC } from "../create-relationship-fields/create-connect-or-create-field"; - -// TODO: refactor this -export function withConnectOrCreateFieldInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - ifUnionMemberEntity, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - composer: SchemaComposer; - userDefinedFieldDirectives: Map; - ifUnionMemberEntity?: ConcreteEntityAdapter; -}): InputTypeComposer | undefined { - if (!relationshipAdapter.nestedOperations.has(RelationshipNestedOperationsOption.CONNECT_OR_CREATE)) { - return; - } - - let targetEntity: ConcreteEntityAdapter | undefined; - if (relationshipAdapter.target instanceof UnionEntityAdapter) { - if (!ifUnionMemberEntity) { - throw new Error("Expected member entity."); - } - targetEntity = ifUnionMemberEntity; - } else { - if (!(relationshipAdapter.target instanceof ConcreteEntityAdapter)) { - throw new Error("Expected concrete target"); - } - targetEntity = relationshipAdapter.target; - } - if ( - !relationshipAdapter.shouldGenerateFieldInputType(ifUnionMemberEntity) && - !relationshipAdapter.shouldGenerateUpdateFieldInputType(ifUnionMemberEntity) - ) { - return; - } - - const hasUniqueFields = targetEntity.uniqueFields.length > 0; - if (hasUniqueFields !== true) { - return; - } - - createOnCreateITC({ - schemaComposer: composer, - relationshipAdapter, - targetEntityAdapter: targetEntity, - userDefinedFieldDirectives, - }); - - // TODO: this should live in the where-fields.ts - composer.getOrCreateITC(targetEntity.operations.connectOrCreateWhereInputTypeName, (tc) => { - tc.addFields((targetEntity as ConcreteEntityAdapter).operations.connectOrCreateWhereInputFieldNames); - }); - - const connectOrCreateName = relationshipAdapter.operations.getConnectOrCreateFieldInputTypeName(targetEntity); - const connectOrCreateFieldInput = composer.getOrCreateITC(connectOrCreateName, (tc) => { - tc.addFields( - relationshipAdapter.operations.getConnectOrCreateInputFields(targetEntity as ConcreteEntityAdapter) || {} - ); - }); - return connectOrCreateFieldInput; -} - -export function withConnectOrCreateInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - deprecatedDirectives, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - composer: SchemaComposer; - userDefinedFieldDirectives: Map; - deprecatedDirectives: Directive[]; -}): InputTypeComposer | undefined { - if (relationshipAdapter.source instanceof UnionEntityAdapter) { - throw new Error("Unexpected union source"); - } - const typeName = relationshipAdapter.source.operations.connectOrCreateInputTypeName; - - const fieldInput = makeConnectOrCreateInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - deprecatedDirectives, - }); - if (!fieldInput) { - return; - } - - const fields = makeConnectOrCreateInputTypeRelationshipField({ - relationshipAdapter, - fieldInput, - deprecatedDirectives, - }); - const connectOrCreateInput = composer.getOrCreateITC(typeName); - - connectOrCreateInput.addFields(fields); - - return connectOrCreateInput; -} - -function makeConnectOrCreateInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - deprecatedDirectives, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - composer: SchemaComposer; - userDefinedFieldDirectives: Map; - deprecatedDirectives: Directive[]; -}): InputTypeComposer | undefined { - if (relationshipAdapter.target instanceof UnionEntityAdapter) { - return withRelationshipConnectOrCreateInputType({ - relationshipAdapter, - composer, - deprecatedDirectives, - userDefinedFieldDirectives, - }); - } - return withConnectOrCreateFieldInputType({ relationshipAdapter, composer, userDefinedFieldDirectives }); -} -function makeConnectOrCreateInputTypeRelationshipField({ - relationshipAdapter, - fieldInput, - deprecatedDirectives, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - fieldInput: InputTypeComposer; - deprecatedDirectives: Directive[]; -}): InputTypeComposerFieldConfigMap { - if (relationshipAdapter.target instanceof UnionEntityAdapter) { - return { - [relationshipAdapter.name]: { - type: fieldInput, - directives: deprecatedDirectives, - }, - }; - } - return { - [relationshipAdapter.name]: { - type: relationshipAdapter.isList ? fieldInput.NonNull.List : fieldInput, - directives: deprecatedDirectives, - }, - }; -} - -function withRelationshipConnectOrCreateInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - deprecatedDirectives, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - composer: SchemaComposer; - userDefinedFieldDirectives: Map; - deprecatedDirectives: Directive[]; -}): InputTypeComposer | undefined { - const typeName = relationshipAdapter.operations.getConnectOrCreateInputTypeName(); - if (!(relationshipAdapter.target instanceof UnionEntityAdapter)) { - throw new Error("Expected union target"); - } - if (composer.has(typeName)) { - return composer.getITC(typeName); - } - const fields = makeUnionConnectOrCreateInputTypeFields({ - relationshipAdapter, - composer, - deprecatedDirectives, - userDefinedFieldDirectives, - }); - if (!Object.keys(fields).length) { - return; - } - const connectOrCreateInput = composer.createInputTC({ name: typeName, fields }); - return connectOrCreateInput; -} - -function makeUnionConnectOrCreateInputTypeFields({ - relationshipAdapter, - composer, - deprecatedDirectives, - userDefinedFieldDirectives, -}: { - relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; - composer: SchemaComposer; - deprecatedDirectives: Directive[]; - userDefinedFieldDirectives: Map; -}): InputTypeComposerFieldConfigMapDefinition { - const fields: InputTypeComposerFieldConfigMapDefinition = {}; - if (!(relationshipAdapter.target instanceof UnionEntityAdapter)) { - throw new Error("Expected union target"); - } - for (const memberEntity of relationshipAdapter.target.concreteEntities) { - const fieldInput = withConnectOrCreateFieldInputType({ - relationshipAdapter, - ifUnionMemberEntity: memberEntity, - composer, - userDefinedFieldDirectives, - }); - if (fieldInput) { - fields[memberEntity.name] = { - type: relationshipAdapter.isList ? fieldInput.NonNull.List : fieldInput, - directives: deprecatedDirectives, - }; - } - } - return fields; -} diff --git a/packages/graphql/src/schema/generation/create-input.ts b/packages/graphql/src/schema/generation/create-input.ts index a83a549529..8c65eaf38c 100644 --- a/packages/graphql/src/schema/generation/create-input.ts +++ b/packages/graphql/src/schema/generation/create-input.ts @@ -30,12 +30,9 @@ import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/Uni import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; -import { DEPRECATE_CONNECT_OR_CREATE } from "../constants"; import { concreteEntityToCreateInputFields } from "../to-compose"; import { withConnectFieldInputType } from "./connect-input"; -import { withConnectOrCreateFieldInputType } from "./connect-or-create-input"; import { withCreateFieldInputType } from "./relation-input"; -import { shouldAddDeprecatedFields } from "./utils"; export function withCreateInputType({ entityAdapter, @@ -157,7 +154,7 @@ function makeRelationshipsInputType({ features, }); } else { - return withFieldInputType({ relationshipAdapter, composer, userDefinedFieldDirectives, features }); + return withFieldInputType({ relationshipAdapter, composer, userDefinedFieldDirectives }); } } @@ -200,7 +197,6 @@ function makeUnionCreateInputTypeFields({ composer, deprecatedDirectives, userDefinedFieldDirectives, - features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; @@ -218,7 +214,6 @@ function makeUnionCreateInputTypeFields({ ifUnionMemberEntity: memberEntity, composer, userDefinedFieldDirectives, - features, }); if (fieldInput) { fields[memberEntity.name] = { @@ -235,13 +230,11 @@ export function withFieldInputType({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, - features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; userDefinedFieldDirectives: Map; ifUnionMemberEntity?: ConcreteEntityAdapter; - features?: Neo4jFeaturesSettings; }): InputTypeComposer | undefined { const typeName = relationshipAdapter.operations.getFieldInputTypeName(ifUnionMemberEntity); if (composer.has(typeName)) { @@ -255,7 +248,6 @@ export function withFieldInputType({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, - features, }); if (!Object.keys(fields).length) { return; @@ -272,45 +264,14 @@ function makeFieldInputTypeFields({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, - features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; userDefinedFieldDirectives: Map; ifUnionMemberEntity?: ConcreteEntityAdapter; - features?: Neo4jFeaturesSettings; }): InputTypeComposerFieldConfigMapDefinition { const fields = {}; - if (shouldAddDeprecatedFields(features, "connectOrCreate")) { - let connectOrCreateFieldInputType: InputTypeComposer | undefined; - if (relationshipAdapter.target instanceof ConcreteEntityAdapter) { - connectOrCreateFieldInputType = withConnectOrCreateFieldInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - }); - } else if (relationshipAdapter.target instanceof UnionEntityAdapter) { - if (!ifUnionMemberEntity) { - throw new Error("Member Entity required."); - } - connectOrCreateFieldInputType = withConnectOrCreateFieldInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - ifUnionMemberEntity, - }); - } - if (connectOrCreateFieldInputType) { - fields["connectOrCreate"] = { - type: relationshipAdapter.isList - ? connectOrCreateFieldInputType.NonNull.List - : connectOrCreateFieldInputType, - directives: [DEPRECATE_CONNECT_OR_CREATE], - }; - } - } - const connectFieldInputType = withConnectFieldInputType({ relationshipAdapter, ifUnionMemberEntity, composer }); if (connectFieldInputType) { fields["connect"] = { diff --git a/packages/graphql/src/schema/generation/fulltext-input.ts b/packages/graphql/src/schema/generation/fulltext-input.ts index f19e28632f..a8f292ff47 100644 --- a/packages/graphql/src/schema/generation/fulltext-input.ts +++ b/packages/graphql/src/schema/generation/fulltext-input.ts @@ -17,88 +17,15 @@ * limitations under the License. */ -import { GraphQLFloat, GraphQLNonNull, GraphQLString } from "graphql"; -import type { - InputTypeComposer, - InputTypeComposerFieldConfigMapDefinition, - ObjectTypeComposer, - SchemaComposer, -} from "graphql-compose"; +import { GraphQLFloat, GraphQLInt, GraphQLNonNull, GraphQLString } from "graphql"; +import type { InputTypeComposer, ObjectTypeComposer, SchemaComposer } from "graphql-compose"; import { SCORE_FIELD } from "../../constants"; import { SortDirection } from "../../graphql/enums/SortDirection"; import { FloatWhere } from "../../graphql/input-objects/FloatWhere"; +import { PageInfo } from "../../graphql/objects/PageInfo"; import type { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; -export function withFullTextInputType({ - concreteEntityAdapter, - composer, -}: { - concreteEntityAdapter: ConcreteEntityAdapter; - composer: SchemaComposer; -}): InputTypeComposer | undefined { - const typeName = concreteEntityAdapter.operations.fullTextInputTypeName; - if (composer.has(typeName)) { - return composer.getITC(typeName); - } - const fields = makeFullTextInputFields({ concreteEntityAdapter, composer }); - const fulltextInputType = composer.createInputTC({ - name: typeName, - fields, - }); - return fulltextInputType; -} - -function makeFullTextInputFields({ - concreteEntityAdapter, - composer, -}: { - concreteEntityAdapter: ConcreteEntityAdapter; - composer: SchemaComposer; -}): InputTypeComposerFieldConfigMapDefinition { - const fields: InputTypeComposerFieldConfigMapDefinition = {}; - if (!concreteEntityAdapter.annotations.fulltext) { - throw new Error("Expected fulltext annotation"); - } - for (const index of concreteEntityAdapter.annotations.fulltext.indexes) { - const indexName = index.indexName || index.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - const fieldInput = withFullTextIndexInputType({ - concreteEntityAdapter, - indexName, - composer, - }); - if (fieldInput) { - fields[indexName] = fieldInput; - } - } - return fields; -} - -function withFullTextIndexInputType({ - composer, - concreteEntityAdapter, - indexName, -}: { - composer: SchemaComposer; - concreteEntityAdapter: ConcreteEntityAdapter; - indexName: string; -}): InputTypeComposer { - const typeName = concreteEntityAdapter.operations.getFullTextIndexInputTypeName(indexName); - if (composer.has(typeName)) { - return composer.getITC(typeName); - } - const indexInput = composer.createInputTC({ - name: typeName, - fields: { - phrase: new GraphQLNonNull(GraphQLString), - }, - }); - return indexInput; -} - -export function withFullTextWhereInputType({ +export function withFulltextWhereInputType({ composer, concreteEntityAdapter, }: { @@ -111,16 +38,16 @@ export function withFullTextWhereInputType({ } const whereInput = composer.createInputTC({ name: typeName, - description: `The input for filtering a fulltext query on an index of ${concreteEntityAdapter.name}`, + description: `The input for filtering a full-text query on an index of ${concreteEntityAdapter.name}`, fields: { [SCORE_FIELD]: FloatWhere.name, - [concreteEntityAdapter.singular]: concreteEntityAdapter.operations.whereInputTypeName, + ["node"]: concreteEntityAdapter.operations.whereInputTypeName, }, }); return whereInput; } -export function withFullTextSortInputType({ +export function withFulltextSortInputType({ composer, concreteEntityAdapter, }: { @@ -133,33 +60,44 @@ export function withFullTextSortInputType({ } const whereInput = composer.createInputTC({ name: typeName, - description: `The input for sorting a fulltext query on an index of ${concreteEntityAdapter.name}`, + description: `The input for sorting a Fulltext query on an index of ${concreteEntityAdapter.name}`, fields: { [SCORE_FIELD]: SortDirection.name, - [concreteEntityAdapter.singular]: concreteEntityAdapter.operations.sortInputTypeName, + node: concreteEntityAdapter.operations.sortInputTypeName, }, }); return whereInput; } -export function withFullTextResultType({ +export function withFulltextResultTypeConnection({ composer, concreteEntityAdapter, }: { composer: SchemaComposer; concreteEntityAdapter: ConcreteEntityAdapter; }): ObjectTypeComposer { - const typeName = concreteEntityAdapter.operations.fulltextTypeNames.result; + const typeName = concreteEntityAdapter.operations.fulltextTypeNames.connection; if (composer.has(typeName)) { return composer.getOTC(typeName); } - const whereInput = composer.createObjectTC({ - name: typeName, - description: `The result of a fulltext search on an index of ${concreteEntityAdapter.name}`, + + const edge = composer.createObjectTC({ + name: concreteEntityAdapter.operations.fulltextTypeNames.edge, fields: { + cursor: new GraphQLNonNull(GraphQLString), + node: `${concreteEntityAdapter.name}!`, [SCORE_FIELD]: new GraphQLNonNull(GraphQLFloat), - [concreteEntityAdapter.singular]: `${concreteEntityAdapter.name}!`, }, }); - return whereInput; + + const connection = composer.createObjectTC({ + name: typeName, + fields: { + totalCount: new GraphQLNonNull(GraphQLInt), + pageInfo: new GraphQLNonNull(PageInfo), + edges: edge.NonNull.List.NonNull, + }, + }); + + return connection; } diff --git a/packages/graphql/src/schema/generation/get-aggregation-filter-from-attribute-type.ts b/packages/graphql/src/schema/generation/get-aggregation-filter-from-attribute-type.ts new file mode 100644 index 0000000000..80cc55e778 --- /dev/null +++ b/packages/graphql/src/schema/generation/get-aggregation-filter-from-attribute-type.ts @@ -0,0 +1,67 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLInputObjectType } from "graphql"; +import { BigIntScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/BigIntScalarAggregationFilters"; +import { DateTimeScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/DateTimeScalarAggregationFilters"; +import { DurationScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/DurationScalarAggregationFilters"; +import { FloatScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/FloatScalarAggregationFilters"; +import { IntScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/IntScalarAggregationFilters"; +import { LocalDateTimeScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/LocalDateTimeScalarAggregationFilters"; +import { LocalTimeScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/LocalTimeScalarAggregationFilters"; +import { StringScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/StringScalarAggregationFilters"; +import { TimeScalarAggregationFilters } from "../../graphql/input-objects/generic-aggregation-filters/TimeScalarAggregationFilters"; +import type { AttributeAdapter } from "../../schema-model/attribute/model-adapters/AttributeAdapter"; + +export function getAggregationFilterFromAttributeType(attribute: AttributeAdapter): GraphQLInputObjectType | string { + if (attribute.typeHelper.isList()) { + throw new Error("List types not available for aggregations"); + } + + if (attribute.typeHelper.isString()) { + return StringScalarAggregationFilters; + } + if (attribute.typeHelper.isInt()) { + return IntScalarAggregationFilters; + } + if (attribute.typeHelper.isFloat()) { + return FloatScalarAggregationFilters; + } + if (attribute.typeHelper.isBigInt()) { + return BigIntScalarAggregationFilters; + } + if (attribute.typeHelper.isTime()) { + return TimeScalarAggregationFilters; + } + + if (attribute.typeHelper.isDateTime()) { + return DateTimeScalarAggregationFilters; + } + if (attribute.typeHelper.isLocalTime()) { + return LocalTimeScalarAggregationFilters; + } + if (attribute.typeHelper.isLocalDateTime()) { + return LocalDateTimeScalarAggregationFilters; + } + if (attribute.typeHelper.isDuration()) { + return DurationScalarAggregationFilters; + } + + throw new Error(`No scalar filter found for attribute ${attribute.type.name}`); +} diff --git a/packages/graphql/src/schema/generation/get-input-filter-from-attribute-type.ts b/packages/graphql/src/schema/generation/get-input-filter-from-attribute-type.ts new file mode 100644 index 0000000000..e0dd4e29cf --- /dev/null +++ b/packages/graphql/src/schema/generation/get-input-filter-from-attribute-type.ts @@ -0,0 +1,163 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLInputType } from "graphql"; +import { + BigIntListFilters, + BigIntScalarFilters, +} from "../../graphql/input-objects/generic-operators/BigIntScalarFilters"; +import { + BooleanListFilters, + BooleanScalarFilters, +} from "../../graphql/input-objects/generic-operators/BooleanScalarFilters"; +import { + CartesianPointFilters, + CartesianPointListFilters, +} from "../../graphql/input-objects/generic-operators/CartesianPointFilters"; +import { DateListFilters, DateScalarFilters } from "../../graphql/input-objects/generic-operators/DateScalarFilters"; +import { + DateTimeListFilters, + DateTimeScalarFilters, +} from "../../graphql/input-objects/generic-operators/DateTimeScalarFilters"; +import { + DurationListFilters, + DurationScalarFilters, +} from "../../graphql/input-objects/generic-operators/DurationScalarFilters"; +import { FloatListFilters, FloatScalarFilters } from "../../graphql/input-objects/generic-operators/FloatScalarFilters"; +import { getIDScalarFilters, IDListFilters } from "../../graphql/input-objects/generic-operators/IDScalarFilters"; +import { IntListFilters, IntScalarFilters } from "../../graphql/input-objects/generic-operators/IntScalarFilters"; +import { + LocalDateTimeListFilters, + LocalDateTimeScalarFilters, +} from "../../graphql/input-objects/generic-operators/LocalDateTimeScalarFilters"; +import { + LocalTimeListFilters, + LocalTimeScalarFilters, +} from "../../graphql/input-objects/generic-operators/LocalTimeScalarFilters"; +import { PointFilters, PointListFilters } from "../../graphql/input-objects/generic-operators/PointFilters"; +import { + getStringScalarFilters, + StringListFilters, +} from "../../graphql/input-objects/generic-operators/StringScalarFilters"; +import { TimeListFilters, TimeScalarFilters } from "../../graphql/input-objects/generic-operators/TimeScalarFilters"; +import type { AttributeAdapter } from "../../schema-model/attribute/model-adapters/AttributeAdapter"; +import type { Neo4jFeaturesSettings } from "../../types"; + +export function getInputFilterFromAttributeType( + attribute: AttributeAdapter, + features?: Neo4jFeaturesSettings +): GraphQLInputType | string { + // NOTE: static types returned here must be added to schema-validation > validateUserDefinition + if (attribute.typeHelper.isBoolean()) { + if (attribute.typeHelper.isList()) { + return BooleanListFilters; + } + return BooleanScalarFilters; + } + if (attribute.typeHelper.isID()) { + if (attribute.typeHelper.isList()) { + return IDListFilters; + } + return getIDScalarFilters(features); + } + if (attribute.typeHelper.isString()) { + if (attribute.typeHelper.isList()) { + return StringListFilters; + } + return getStringScalarFilters(features); + } + if (attribute.typeHelper.isInt()) { + if (attribute.typeHelper.isList()) { + return IntListFilters; + } + return IntScalarFilters; + } + if (attribute.typeHelper.isFloat()) { + if (attribute.typeHelper.isList()) { + return FloatListFilters; + } + return FloatScalarFilters; + } + if (attribute.typeHelper.isBigInt()) { + if (attribute.typeHelper.isList()) { + return BigIntListFilters; + } + return BigIntScalarFilters; + } + if (attribute.typeHelper.isTime()) { + if (attribute.typeHelper.isList()) { + return TimeListFilters; + } + return TimeScalarFilters; + } + if (attribute.typeHelper.isPoint()) { + if (attribute.typeHelper.isList()) { + return PointListFilters; + } + return PointFilters; + } + if (attribute.typeHelper.isCartesianPoint()) { + if (attribute.typeHelper.isList()) { + return CartesianPointListFilters; + } + return CartesianPointFilters; + } + if (attribute.typeHelper.isDateTime()) { + if (attribute.typeHelper.isList()) { + return DateTimeListFilters; + } + return DateTimeScalarFilters; + } + if (attribute.typeHelper.isLocalTime()) { + if (attribute.typeHelper.isList()) { + return LocalTimeListFilters; + } + return LocalTimeScalarFilters; + } + if (attribute.typeHelper.isLocalDateTime()) { + if (attribute.typeHelper.isList()) { + return LocalDateTimeListFilters; + } + return LocalDateTimeScalarFilters; + } + if (attribute.typeHelper.isDuration()) { + if (attribute.typeHelper.isList()) { + return DurationListFilters; + } + return DurationScalarFilters; + } + if (attribute.typeHelper.isDate()) { + if (attribute.typeHelper.isList()) { + return DateListFilters; + } + return DateScalarFilters; + } + + if (attribute.typeHelper.isEnum()) { + const filtersName = attribute.typeHelper.isList() ? "ListEnumScalarFilters" : "EnumScalarFilters"; + return `${attribute.getTypeName()}${filtersName}`; + } + + if (attribute.typeHelper.isUserScalar()) { + const filtersName = attribute.typeHelper.isList() ? "ListScalarFilters" : "ScalarFilters"; + return `${attribute.getTypeName()}${filtersName}`; + } + + throw new Error(`No scalar filter found for attribute ${attribute.type.name}`); +} diff --git a/packages/graphql/src/schema/generation/get-mutation-input-from-attribute-type.ts b/packages/graphql/src/schema/generation/get-mutation-input-from-attribute-type.ts new file mode 100644 index 0000000000..dcdd9ee876 --- /dev/null +++ b/packages/graphql/src/schema/generation/get-mutation-input-from-attribute-type.ts @@ -0,0 +1,181 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLInputType } from "graphql"; +import { + BigIntListMutations, + BigIntScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/BigIntScalarMutations"; +import { + BooleanListMutations, + BooleanScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/BooleanScalarMutations"; +import { + CartesianPointListMutations, + CartesianPointMutations, +} from "../../graphql/input-objects/generic-mutation-operations/CartesianPointMutations"; +import { + DateListMutations, + DateScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/DateScalarMutations"; +import { + DateTimeListMutations, + DateTimeScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/DateTimeScalarMutations"; +import { + DurationListMutations, + DurationScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/DurationScalarMutations"; +import { + FloatListMutations, + FloatScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/FloatScalarMutations"; +import { + IDListMutations, + IDScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/IDScalarMutations"; +import { + IntListMutations, + IntScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/IntScalarMutations"; +import { + LocalDateTimeListMutations, + LocalDateTimeScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/LocalDateTimeScalarMutations"; +import { + LocalTimeListMutations, + LocalTimeScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/LocalTimeScalarMutations"; +import { + PointListMutations, + PointMutations, +} from "../../graphql/input-objects/generic-mutation-operations/PointMutations"; +import { + StringListMutations, + StringScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/StringScalarMutations"; +import { + TimeListMutations, + TimeScalarMutations, +} from "../../graphql/input-objects/generic-mutation-operations/TimeScalarMutations"; +import type { AttributeAdapter } from "../../schema-model/attribute/model-adapters/AttributeAdapter"; + +export function getMutationInputFromAttributeType(attribute: AttributeAdapter): GraphQLInputType | string { + // // NOTE: static types returned here must be added to schema-validation > validateUserDefinition + if (attribute.typeHelper.isBoolean()) { + if (attribute.typeHelper.isList()) { + return BooleanListMutations; + } + return BooleanScalarMutations; + } + if (attribute.typeHelper.isID()) { + if (attribute.typeHelper.isList()) { + return IDListMutations; + } + return IDScalarMutations; + } + if (attribute.typeHelper.isString()) { + if (attribute.typeHelper.isList()) { + return StringListMutations; + } + return StringScalarMutations; + } + if (attribute.typeHelper.isInt()) { + if (attribute.typeHelper.isList()) { + return IntListMutations; + } + return IntScalarMutations; + } + if (attribute.typeHelper.isFloat()) { + if (attribute.typeHelper.isList()) { + return FloatListMutations; + } + return FloatScalarMutations; + } + if (attribute.typeHelper.isBigInt()) { + if (attribute.typeHelper.isList()) { + return BigIntListMutations; + } + return BigIntScalarMutations; + } + if (attribute.typeHelper.isTime()) { + if (attribute.typeHelper.isList()) { + return TimeListMutations; + } + return TimeScalarMutations; + } + if (attribute.typeHelper.isPoint()) { + if (attribute.typeHelper.isList()) { + return PointListMutations; + } + return PointMutations; + } + if (attribute.typeHelper.isCartesianPoint()) { + if (attribute.typeHelper.isList()) { + return CartesianPointListMutations; + } + return CartesianPointMutations; + } + if (attribute.typeHelper.isDateTime()) { + if (attribute.typeHelper.isList()) { + return DateTimeListMutations; + } + return DateTimeScalarMutations; + } + if (attribute.typeHelper.isLocalTime()) { + if (attribute.typeHelper.isList()) { + return LocalTimeListMutations; + } + return LocalTimeScalarMutations; + } + if (attribute.typeHelper.isLocalDateTime()) { + if (attribute.typeHelper.isList()) { + return LocalDateTimeListMutations; + } + return LocalDateTimeScalarMutations; + } + if (attribute.typeHelper.isDuration()) { + if (attribute.typeHelper.isList()) { + return DurationListMutations; + } + return DurationScalarMutations; + } + if (attribute.typeHelper.isDate()) { + if (attribute.typeHelper.isList()) { + return DateListMutations; + } + return DateScalarMutations; + } + + if (attribute.typeHelper.isEnum()) { + if (attribute.typeHelper.isList()) { + return `${attribute.getTypeName()}ListEnumScalarMutations`; + } + return `${attribute.getTypeName()}EnumScalarMutations`; + } + + if (attribute.typeHelper.isUserScalar()) { + if (attribute.typeHelper.isList()) { + return `${attribute.getTypeName()}ListScalarMutations`; + } + return `${attribute.getTypeName()}ScalarMutations`; + } + + throw new Error(`No scalar mutation found for attribute ${attribute.type.name}`); +} diff --git a/packages/graphql/src/schema/generation/sort-and-options-input.ts b/packages/graphql/src/schema/generation/sort-and-options-input.ts index c61b2fcb85..f6067ae53e 100644 --- a/packages/graphql/src/schema/generation/sort-and-options-input.ts +++ b/packages/graphql/src/schema/generation/sort-and-options-input.ts @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { GraphQLInt, type DirectiveNode } from "graphql"; +import { type DirectiveNode } from "graphql"; import type { InputTypeComposer, InputTypeComposerFieldConfigMapDefinition, SchemaComposer } from "graphql-compose"; import { DEPRECATED } from "../../constants"; import { SortDirection } from "../../graphql/enums/SortDirection"; @@ -25,32 +25,6 @@ import type { InterfaceEntityAdapter } from "../../schema-model/entity/model-ada import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { graphqlDirectivesToCompose } from "../to-compose"; -export function withOptionsInputType({ - entityAdapter, - userDefinedFieldDirectives, - composer, -}: { - entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter; - userDefinedFieldDirectives: Map; - composer: SchemaComposer; -}): InputTypeComposer { - const optionsInputType = makeOptionsInput({ entityAdapter, composer }); - const sortInput = makeSortInput({ entityAdapter, userDefinedFieldDirectives, composer }); - if (!sortInput) { - return optionsInputType; - } - // TODO: Concrete vs Abstract discrepancy - // is this intended? For ConcreteEntity is NonNull, for InterfaceEntity is nullable - const sortFieldType = sortInput.NonNull.List; - optionsInputType.addFields({ - sort: { - description: `Specify one or more ${entityAdapter.operations.sortInputTypeName} objects to sort ${entityAdapter.upperFirstPlural} by. The sorts will be applied in the order in which they are arranged in the array.`, - type: sortFieldType, - }, - }); - return optionsInputType; -} - export function withSortInputType({ relationshipAdapter, userDefinedFieldDirectives, @@ -130,17 +104,3 @@ export function makeSortInput({ } return sortInput; } - -function makeOptionsInput({ - entityAdapter, - composer, -}: { - entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter; - composer: SchemaComposer; -}): InputTypeComposer { - const optionsInput = composer.createInputTC({ - name: entityAdapter.operations.optionsInputTypeName, - fields: { limit: GraphQLInt, offset: GraphQLInt }, - }); - return optionsInput; -} diff --git a/packages/graphql/src/schema/generation/update-input.ts b/packages/graphql/src/schema/generation/update-input.ts index 8b2ef75fda..a5459c8d79 100644 --- a/packages/graphql/src/schema/generation/update-input.ts +++ b/packages/graphql/src/schema/generation/update-input.ts @@ -33,9 +33,9 @@ import { RelationshipAdapter } from "../../schema-model/relationship/model-adapt import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { Neo4jFeaturesSettings } from "../../types"; import { ensureNonEmptyInput } from "../ensure-non-empty-input"; +import type { AdditionalFieldsCallback } from "../to-compose"; import { concreteEntityToUpdateInputFields, withArrayOperators, withMathOperators } from "../to-compose"; import { withConnectFieldInputType } from "./connect-input"; -import { withConnectOrCreateFieldInputType } from "./connect-or-create-input"; import { withConnectionWhereInputType } from "./connection-where-input"; import { withDeleteFieldInputType } from "./delete-input"; import { withDisconnectFieldInputType } from "./disconnect-input"; @@ -51,7 +51,7 @@ export function withUpdateInputType({ entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter | RelationshipAdapter; userDefinedFieldDirectives: Map; composer: SchemaComposer; - features?: Neo4jFeaturesSettings; + features: Neo4jFeaturesSettings | undefined; }): InputTypeComposer { const inputTypeName = entityAdapter instanceof RelationshipAdapter @@ -67,11 +67,16 @@ export function withUpdateInputType({ }); if (entityAdapter instanceof ConcreteEntityAdapter || entityAdapter instanceof RelationshipAdapter) { + const additionalFields: AdditionalFieldsCallback[] = []; + if (shouldAddDeprecatedFields(features, "mutationOperations")) { + additionalFields.push(withMathOperators(), withArrayOperators()); + } + updateInputType.addFields( concreteEntityToUpdateInputFields({ objectFields: entityAdapter.updateInputFields, userDefinedFieldDirectives, - additionalFieldsCallbacks: [withMathOperators(), withArrayOperators()], + additionalFieldsCallbacks: additionalFields, features, }) ); @@ -82,11 +87,16 @@ export function withUpdateInputType({ ensureNonEmptyInput(composer, updateInputType); } + const additionalFields: AdditionalFieldsCallback[] = []; + if (shouldAddDeprecatedFields(features, "mutationOperations")) { + additionalFields.push(withMathOperators()); + } + updateInputType.addFields( concreteEntityToUpdateInputFields({ objectFields: entityAdapter.updateInputFields, userDefinedFieldDirectives, - additionalFieldsCallbacks: [withMathOperators()], + additionalFieldsCallbacks: additionalFields, features, }) ); @@ -124,6 +134,7 @@ export function augmentUpdateInputTypeWithUpdateFieldInput({ entityAdapter: relationshipAdapter.source, userDefinedFieldDirectives, composer, + features, }); const relationshipField = makeUpdateInputTypeRelationshipField({ relationshipAdapter, @@ -233,16 +244,10 @@ function makeUpdateFieldInputTypeFields({ }): InputTypeComposerFieldConfigMapDefinition { const fields = {}; - let connectOrCreateFieldInputType: InputTypeComposer | undefined; let connectionWhereInputType: InputTypeComposer | string | undefined; const relationshipTarget = relationshipAdapter.target; if (relationshipTarget instanceof ConcreteEntityAdapter) { connectionWhereInputType = relationshipAdapter.operations.getConnectionWhereTypename(); - connectOrCreateFieldInputType = withConnectOrCreateFieldInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - }); } else if (relationshipTarget instanceof InterfaceEntityAdapter) { connectionWhereInputType = relationshipAdapter.operations.getConnectionWhereTypename(); } else { @@ -254,12 +259,6 @@ function makeUpdateFieldInputTypeFields({ memberEntity: ifUnionMemberEntity, composer, }); - connectOrCreateFieldInputType = withConnectOrCreateFieldInputType({ - relationshipAdapter, - composer, - userDefinedFieldDirectives, - ifUnionMemberEntity, - }); } if (connectionWhereInputType) { fields["where"] = { @@ -267,14 +266,7 @@ function makeUpdateFieldInputTypeFields({ directives: [], }; } - if (connectOrCreateFieldInputType && shouldAddDeprecatedFields(features, "connectOrCreate")) { - fields["connectOrCreate"] = { - type: relationshipAdapter.isList - ? connectOrCreateFieldInputType.NonNull.List - : connectOrCreateFieldInputType, - directives: [], - }; - } + const connectFieldInputType = withConnectFieldInputType({ relationshipAdapter, ifUnionMemberEntity, composer }); if (connectFieldInputType) { fields["connect"] = { @@ -311,6 +303,7 @@ function makeUpdateFieldInputTypeFields({ ifUnionMemberEntity, composer, userDefinedFieldDirectives, + features, }); if (updateFieldInputType) { fields["update"] = { @@ -405,11 +398,13 @@ function withUpdateConnectionFieldInputType({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, + features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; userDefinedFieldDirectives: Map; ifUnionMemberEntity?: ConcreteEntityAdapter; + features: Neo4jFeaturesSettings | undefined; }): InputTypeComposer | undefined { const typeName = relationshipAdapter.operations.getUpdateConnectionInputTypename(ifUnionMemberEntity); if (!relationshipAdapter.nestedOperations.has(RelationshipNestedOperationsOption.UPDATE)) { @@ -423,6 +418,7 @@ function withUpdateConnectionFieldInputType({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, + features, }); const updateFieldInput = composer.createInputTC({ name: typeName, fields }); @@ -433,11 +429,13 @@ function makeUpdateConnectionFieldInputTypeFields({ composer, userDefinedFieldDirectives, ifUnionMemberEntity, + features, }: { relationshipAdapter: RelationshipAdapter | RelationshipDeclarationAdapter; composer: SchemaComposer; userDefinedFieldDirectives: Map; ifUnionMemberEntity?: ConcreteEntityAdapter; + features: Neo4jFeaturesSettings | undefined; }): InputTypeComposerFieldConfigMapDefinition { const fields: InputTypeComposerFieldConfigMapDefinition = {}; if (relationshipAdapter.target instanceof UnionEntityAdapter) { @@ -448,6 +446,7 @@ function makeUpdateConnectionFieldInputTypeFields({ entityAdapter: ifUnionMemberEntity, userDefinedFieldDirectives, composer, + features, }); fields["node"] = updateInputType; } else { diff --git a/packages/graphql/src/schema/generation/where-input.ts b/packages/graphql/src/schema/generation/where-input.ts index b8ff6aad27..413109382a 100644 --- a/packages/graphql/src/schema/generation/where-input.ts +++ b/packages/graphql/src/schema/generation/where-input.ts @@ -29,17 +29,13 @@ import type { EntityAdapter } from "../../schema-model/entity/EntityAdapter"; import { ConcreteEntityAdapter } from "../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { InterfaceEntityAdapter } from "../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import { UnionEntityAdapter } from "../../schema-model/entity/model-adapters/UnionEntityAdapter"; -import { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; +import type { RelationshipAdapter } from "../../schema-model/relationship/model-adapters/RelationshipAdapter"; import type { RelationshipDeclarationAdapter } from "../../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; +import { isUnionEntity } from "../../translate/queryAST/utils/is-union-entity"; import type { Neo4jFeaturesSettings } from "../../types"; -import { DEPRECATE_IMPLICIT_EQUAL_FILTERS, DEPRECATE_TYPENAME_IN } from "../constants"; import { getWhereFieldsForAttributes } from "../get-where-fields"; import { withAggregateInputType } from "./aggregate-types"; -import { - augmentWhereInputTypeWithConnectionFields, - augmentWhereInputTypeWithRelationshipFields, -} from "./augment-where-input"; -import { shouldAddDeprecatedFields } from "./utils"; +import { augmentWhereInputWithRelationshipFilters } from "./augment-where-input"; function isEmptyObject(obj: Record): boolean { return !Object.keys(obj).length; @@ -48,24 +44,11 @@ function isEmptyObject(obj: Record): boolean { export function withUniqueWhereInputType({ concreteEntityAdapter, composer, - features, }: { concreteEntityAdapter: ConcreteEntityAdapter; composer: SchemaComposer; - features?: Neo4jFeaturesSettings; }): InputTypeComposer { const uniqueWhereFields: InputTypeComposerFieldConfigMapDefinition = {}; - for (const attribute of concreteEntityAdapter.uniqueFields) { - if (shouldAddDeprecatedFields(features, "implicitEqualFilters")) { - uniqueWhereFields[attribute.name] = { - type: attribute.getFieldTypeName(), - directives: [DEPRECATE_IMPLICIT_EQUAL_FILTERS], - }; - } - uniqueWhereFields[`${attribute.name}_EQ`] = { - type: attribute.getFieldTypeName(), - }; - } const uniqueWhereInputType = composer.createInputTC({ name: concreteEntityAdapter.operations.uniqueWhereInputTypeName, @@ -105,32 +88,34 @@ export function withWhereInputType({ if (composer.has(typeName)) { return composer.getITC(typeName); } + const whereFields = makeWhereFields({ entityAdapter, userDefinedFieldDirectives, features, ignoreCypherFieldFilters, + composer, }); + if (returnUndefinedIfEmpty && isEmptyObject(whereFields)) { return undefined; } + const whereInputType = composer.createInputTC({ name: typeName, fields: whereFields, }); - const allowNesting = - alwaysAllowNesting || - entityAdapter instanceof ConcreteEntityAdapter || - entityAdapter instanceof RelationshipAdapter || - entityAdapter instanceof InterfaceEntityAdapter; + const allowNesting = alwaysAllowNesting || !isUnionEntity(entityAdapter); if (allowNesting) { addLogicalOperatorsToWhereInputType(whereInputType); } + if (entityAdapter instanceof ConcreteEntityAdapter && entityAdapter.isGlobalNode()) { whereInputType.addFields({ id: GraphQLID }); } + if (entityAdapter instanceof InterfaceEntityAdapter) { const enumValues = Object.fromEntries( entityAdapter.concreteEntities.map((concreteEntity) => [ @@ -143,12 +128,6 @@ export function withWhereInputType({ name: entityAdapter.operations.implementationEnumTypename, values: enumValues, }); - if (shouldAddDeprecatedFields(features, "typename_IN")) { - whereInputType.addFields({ - typename_IN: { type: interfaceImplementation.NonNull.List, directives: [DEPRECATE_TYPENAME_IN] }, - }); - } - whereInputType.addFields({ typename: { type: interfaceImplementation.NonNull.List } }); } } @@ -160,11 +139,13 @@ function makeWhereFields({ userDefinedFieldDirectives, features, ignoreCypherFieldFilters, + composer, }: { entityAdapter: EntityAdapter | RelationshipAdapter; userDefinedFieldDirectives?: Map; features: Neo4jFeaturesSettings | undefined; ignoreCypherFieldFilters: boolean; + composer: SchemaComposer }): InputTypeComposerFieldConfigMapDefinition { if (entityAdapter instanceof UnionEntityAdapter) { const fields: InputTypeComposerFieldConfigMapDefinition = {}; @@ -179,6 +160,8 @@ function makeWhereFields({ userDefinedFieldDirectives, features, ignoreCypherFieldFilters, + composer, + }); } @@ -198,11 +181,13 @@ export function withSourceWhereInputType({ const relationshipTarget = relationshipAdapter.target; const relationshipSource = relationshipAdapter.source; const whereInput = composer.getITC(relationshipSource.operations.whereInputTypeName); - const fields = augmentWhereInputTypeWithRelationshipFields(relationshipAdapter, deprecatedDirectives); - whereInput.addFields(fields); - - const connectionFields = augmentWhereInputTypeWithConnectionFields(relationshipAdapter, deprecatedDirectives); - whereInput.addFields(connectionFields); + augmentWhereInputWithRelationshipFilters({ + whereInput, + relationshipAdapter, + deprecatedDirectives, + composer, + features, + }); // TODO: Current unions are not supported as relationship targets beyond the above fields if (relationshipTarget instanceof UnionEntityAdapter) { diff --git a/packages/graphql/src/schema/get-definition-nodes.ts b/packages/graphql/src/schema/get-definition-nodes.ts deleted file mode 100644 index a3b0e16fda..0000000000 --- a/packages/graphql/src/schema/get-definition-nodes.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Debug from "debug"; -import type { - DirectiveDefinitionNode, - DocumentNode, - EnumTypeDefinitionNode, - InputObjectTypeDefinitionNode, - InterfaceTypeDefinitionNode, - ObjectTypeDefinitionNode, - ScalarTypeDefinitionNode, - SchemaExtensionNode, - UnionTypeDefinitionNode, -} from "graphql"; -import { Kind } from "graphql"; -import { isRootType } from "../utils/is-root-type"; -import { DEBUG_GENERATE } from "../constants"; - -const debug = Debug(DEBUG_GENERATE); - -export type DefinitionNodes = { - objectTypes: ObjectTypeDefinitionNode[]; - scalarTypes: ScalarTypeDefinitionNode[]; - enumTypes: EnumTypeDefinitionNode[]; - inputObjectTypes: InputObjectTypeDefinitionNode[]; - interfaceTypes: InterfaceTypeDefinitionNode[]; - directives: DirectiveDefinitionNode[]; - unionTypes: UnionTypeDefinitionNode[]; - schemaExtensions: SchemaExtensionNode[]; - operations: ObjectTypeDefinitionNode[]; -}; - -export function getDefinitionNodes(document: DocumentNode): DefinitionNodes { - return document.definitions.reduce( - (definitionNodes, definition) => { - switch (definition.kind) { - case Kind.SCALAR_TYPE_DEFINITION: - definitionNodes.scalarTypes.push(definition); - break; - case Kind.OBJECT_TYPE_DEFINITION: - if (!isRootType(definition)) { - definitionNodes.objectTypes.push(definition); - } else { - definitionNodes.operations.push(definition); - } - break; - case Kind.ENUM_TYPE_DEFINITION: - definitionNodes.enumTypes.push(definition); - break; - case Kind.INPUT_OBJECT_TYPE_DEFINITION: - definitionNodes.inputObjectTypes.push(definition); - break; - case Kind.INTERFACE_TYPE_DEFINITION: - definitionNodes.interfaceTypes.push(definition); - break; - case Kind.DIRECTIVE_DEFINITION: - definitionNodes.directives.push(definition); - break; - case Kind.UNION_TYPE_DEFINITION: - definitionNodes.unionTypes.push(definition); - break; - case Kind.SCHEMA_EXTENSION: - definitionNodes.schemaExtensions.push(definition); - break; - case Kind.SCHEMA_DEFINITION: - break; - default: - debug(`Ignoring definition kind ${definition.kind}`); - } - - return definitionNodes; - }, - { - objectTypes: [], - inputObjectTypes: [], - enumTypes: [], - scalarTypes: [], - interfaceTypes: [], - directives: [], - unionTypes: [], - schemaExtensions: [], - operations: [], - } - ); -} diff --git a/packages/graphql/src/schema/get-nodes.ts b/packages/graphql/src/schema/get-nodes.ts index 8ce633caf7..93fe13276e 100644 --- a/packages/graphql/src/schema/get-nodes.ts +++ b/packages/graphql/src/schema/get-nodes.ts @@ -22,12 +22,11 @@ import type { NamedTypeNode } from "graphql"; import { Node } from "../classes"; import type { LimitDirective } from "../classes/LimitDirective"; import type { NodeDirective } from "../classes/NodeDirective"; -import type { FullText, Neo4jGraphQLCallbacks } from "../types"; -import { asArray, haveSharedElement } from "../utils/utils"; -import type { DefinitionNodes } from "./get-definition-nodes"; -import getObjFieldMeta from "./get-obj-field-meta"; +import type { DefinitionCollection } from "../schema-model/parser/definition-collection"; +import type { Neo4jGraphQLCallbacks } from "../types"; +import { asArray } from "../utils/utils"; +import { getObjFieldMeta } from "./get-obj-field-meta"; import parseNodeDirective from "./parse-node-directive"; -import parseFulltextDirective from "./parse/parse-fulltext-directive"; import { parseLimitDirective } from "./parse/parse-limit-directive"; import parsePluralDirective from "./parse/parse-plural-directive"; @@ -41,7 +40,7 @@ type Nodes = { }; function getNodes( - definitionNodes: DefinitionNodes, + definitionCollection: DefinitionCollection, options: { callbacks?: Neo4jGraphQLCallbacks; userCustomResolvers?: IResolvers | Array; @@ -49,128 +48,105 @@ function getNodes( ): Nodes { let pointInTypeDefs = false; let cartesianPointInTypeDefs = false; - let floatWhereInTypeDefs = false; + const floatWhereInTypeDefs = false; const relationshipPropertyInterfaceNames = new Set(); const interfaceRelationshipNames = new Set(); - const nodes = definitionNodes.objectTypes - .filter((definition) => { - const directiveNames = (definition.directives || []).map((d) => d.name.value); - const excludeObjectFromNode = haveSharedElement(directiveNames, ["relationshipProperties"]); - return !excludeObjectFromNode; - }) - .map((definition) => { - const otherDirectives = (definition.directives || []).filter( - (x) => - ![ - "authorization", - "authentication", - "exclude", - "node", - "fulltext", - "limit", - "plural", - "shareable", - "subscriptionsAuthorization", - "deprecated", - "query", - "mutation", - "subscription", - "jwt", - ].includes(x.name.value) - ); - const propagatedDirectives = (definition.directives || []).filter((x) => - ["deprecated", "shareable"].includes(x.name.value) - ); - - const nodeDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "node"); - const pluralDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "plural"); - const fulltextDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "fulltext"); - const limitDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "limit"); - const nodeInterfaces = [...(definition.interfaces || [])] as NamedTypeNode[]; - - let nodeDirective: NodeDirective; - if (nodeDirectiveDefinition) { - nodeDirective = parseNodeDirective(nodeDirectiveDefinition); - } - - const userCustomResolvers = asArray(options.userCustomResolvers); - const customResolvers = userCustomResolvers.find((r) => !!r[definition.name.value])?.[ - definition.name.value - ] as IResolvers; - - const nodeFields = getObjFieldMeta({ - obj: definition, - enums: definitionNodes.enumTypes, - interfaces: definitionNodes.interfaceTypes, - objects: definitionNodes.objectTypes, - scalars: definitionNodes.scalarTypes, - unions: definitionNodes.unionTypes, - callbacks: options.callbacks, - customResolvers, - }); - - let fulltextDirective: FullText; - if (fulltextDirectiveDefinition) { - fulltextDirective = parseFulltextDirective({ - directive: fulltextDirectiveDefinition, - nodeFields, - definition, - }); - floatWhereInTypeDefs = true; - } - - let limitDirective: LimitDirective | undefined; - if (limitDirectiveDefinition) { - limitDirective = parseLimitDirective({ - directive: limitDirectiveDefinition, - definition, - }); - } + const nodes: Node[] = []; + for (const definition of definitionCollection.nodes.values()) { + const otherDirectives = (definition.directives || []).filter( + (x) => + ![ + "authorization", + "authentication", + "exclude", + "node", + "fulltext", + "limit", + "plural", + "shareable", + "subscriptionsAuthorization", + "deprecated", + "query", + "mutation", + "subscription", + "jwt", + ].includes(x.name.value) + ); + const propagatedDirectives = (definition.directives || []).filter((x) => + ["deprecated", "shareable"].includes(x.name.value) + ); + + const nodeDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "node"); + const pluralDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "plural"); + const limitDirectiveDefinition = (definition.directives || []).find((x) => x.name.value === "limit"); + const nodeInterfaces = [...(definition.interfaces || [])] as NamedTypeNode[]; + + let nodeDirective: NodeDirective; + if (nodeDirectiveDefinition) { + nodeDirective = parseNodeDirective(nodeDirectiveDefinition); + } + + const userCustomResolvers = asArray(options.userCustomResolvers); + const customResolvers = userCustomResolvers.find((r) => !!r[definition.name.value])?.[ + definition.name.value + ] as IResolvers; + + const nodeFields = getObjFieldMeta({ + obj: definition, + definitionCollection, + interfaces: [...definitionCollection.interfaceTypes.values()], + callbacks: options.callbacks, + customResolvers, + }); - nodeFields.relationFields.forEach((relationship) => { - if (relationship.properties) { - relationshipPropertyInterfaceNames.add(relationship.properties); - } - if (relationship.interface) { - interfaceRelationshipNames.add(relationship.typeMeta.name); - } + let limitDirective: LimitDirective | undefined; + if (limitDirectiveDefinition) { + limitDirective = parseLimitDirective({ + directive: limitDirectiveDefinition, + definition, }); + } - if (!pointInTypeDefs) { - pointInTypeDefs = nodeFields.pointFields.some((field) => field.typeMeta.name === "Point"); + nodeFields.relationFields.forEach((relationship) => { + if (relationship.properties) { + relationshipPropertyInterfaceNames.add(relationship.properties); } - if (!cartesianPointInTypeDefs) { - cartesianPointInTypeDefs = nodeFields.pointFields.some( - (field) => field.typeMeta.name === "CartesianPoint" - ); + if (relationship.interface) { + interfaceRelationshipNames.add(relationship.typeMeta.name); } + }); - const globalIdFields = nodeFields.primitiveFields.filter((field) => field.isGlobalIdField); - - const globalIdField = globalIdFields[0]; - - const node = new Node({ - name: definition.name.value, - interfaces: nodeInterfaces, - otherDirectives, - propagatedDirectives, - ...nodeFields, - // @ts-ignore we can be sure it's defined - nodeDirective, - // @ts-ignore we can be sure it's defined - fulltextDirective, - limitDirective, - description: definition.description?.value, - isGlobalNode: Boolean(globalIdField), - globalIdField: globalIdField?.fieldName, - globalIdFieldIsInt: globalIdField?.typeMeta?.name === "Int", - plural: parsePluralDirective(pluralDirectiveDefinition), - }); - - return node; + if (!pointInTypeDefs) { + pointInTypeDefs = nodeFields.pointFields.some((field) => field.typeMeta.name === "Point"); + } + if (!cartesianPointInTypeDefs) { + cartesianPointInTypeDefs = nodeFields.pointFields.some((field) => field.typeMeta.name === "CartesianPoint"); + } + + const globalIdFields = nodeFields.primitiveFields.filter((field) => field.isGlobalIdField); + + const globalIdField = globalIdFields[0]; + + const node = new Node({ + name: definition.name.value, + interfaces: nodeInterfaces, + otherDirectives, + propagatedDirectives, + ...nodeFields, + // @ts-ignore we can be sure it's defined + nodeDirective, + limitDirective, + description: definition.description?.value, + isGlobalNode: Boolean(globalIdField), + globalIdField: globalIdField?.fieldName, + globalIdFieldIsInt: globalIdField?.typeMeta?.name === "Int", + plural: parsePluralDirective(pluralDirectiveDefinition), }); + + nodes.push(node); + } return { nodes, pointInTypeDefs, diff --git a/packages/graphql/src/schema/get-obj-field-meta.ts b/packages/graphql/src/schema/get-obj-field-meta.ts index 53431221e2..0f1362bf1e 100644 --- a/packages/graphql/src/schema/get-obj-field-meta.ts +++ b/packages/graphql/src/schema/get-obj-field-meta.ts @@ -21,18 +21,16 @@ import type { IResolvers } from "@graphql-tools/utils"; import type { BooleanValueNode, DirectiveNode, - EnumTypeDefinitionNode, EnumValueNode, InterfaceTypeDefinitionNode, ListValueNode, NamedTypeNode, ObjectTypeDefinitionNode, - ScalarTypeDefinitionNode, StringValueNode, - UnionTypeDefinitionNode, } from "graphql"; import { Kind } from "graphql"; import { SCALAR_TYPES, SPATIAL_TYPES, TEMPORAL_SCALAR_TYPES } from "../constants"; +import type { DefinitionCollection } from "../schema-model/parser/definition-collection"; import { parseArgumentsFromUnknownDirective } from "../schema-model/parser/parse-arguments"; import { parseValueNode } from "../schema-model/parser/parse-value-node"; import type { @@ -62,7 +60,6 @@ import { getCypherMeta } from "./get-cypher-meta"; import getFieldTypeMeta from "./get-field-type-meta"; import { getPopulatedByMeta } from "./get-populated-by-meta"; import getRelationshipMeta from "./get-relationship-meta"; -import getUniqueMeta from "./parse/get-unique-meta"; export interface ObjectFields { relationFields: RelationField[]; @@ -79,26 +76,22 @@ export interface ObjectFields { customResolverFields: CustomResolverField[]; } -function getObjFieldMeta({ +export function getObjFieldMeta({ obj, - objects, + definitionCollection, interfaces, - scalars, - unions, - enums, callbacks, customResolvers, }: { obj: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode; - objects: ObjectTypeDefinitionNode[]; + definitionCollection: DefinitionCollection; interfaces: InterfaceTypeDefinitionNode[]; - unions: UnionTypeDefinitionNode[]; - scalars: ScalarTypeDefinitionNode[]; - enums: EnumTypeDefinitionNode[]; callbacks?: Neo4jGraphQLCallbacks; customResolvers?: IResolvers | Array; }): ObjectFields { const objInterfaceNames = [...(obj.interfaces || [])] as NamedTypeNode[]; + const objects = [...definitionCollection.nodes.values()]; + const unions = [...definitionCollection.unionTypes.values()]; const objInterfaces = interfaces.filter((i) => objInterfaceNames.map((n) => n.name.value).includes(i.name.value)); return obj?.fields?.reduce( @@ -141,13 +134,12 @@ function getObjFieldMeta({ const selectableDirective = directives.find((x) => x.name.value === "selectable"); const settableDirective = directives.find((x) => x.name.value === "settable"); const filterableDirective = directives.find((x) => x.name.value === "filterable"); - const unique = getUniqueMeta(directives, obj, field.name.value); - const fieldInterface = interfaces.find((x) => x.name.value === typeMeta.name); - const fieldUnion = unions.find((x) => x.name.value === typeMeta.name); - const fieldScalar = scalars.find((x) => x.name.value === typeMeta.name); - const fieldEnum = enums.find((x) => x.name.value === typeMeta.name); - const fieldObject = objects.find((x) => x.name.value === typeMeta.name); + const fieldInterface = definitionCollection.interfaceTypes.get(typeMeta.name); + const fieldUnion = definitionCollection.unionTypes.get(typeMeta.name); + const fieldScalar = definitionCollection.scalarTypes.get(typeMeta.name); + const fieldEnum = definitionCollection.enumTypes.get(typeMeta.name); + const fieldObject = definitionCollection.objectTypes.get(typeMeta.name); const fieldTemporal = TEMPORAL_SCALAR_TYPES.includes(typeMeta.name); const fieldPoint = SPATIAL_TYPES.includes(typeMeta.name); @@ -175,7 +167,6 @@ function getObjFieldMeta({ "coalesce", "timestamp", "alias", - "unique", "callback", "populatedBy", "jwtClaim", @@ -188,7 +179,6 @@ function getObjFieldMeta({ ), arguments: [...(field.arguments || [])], description: field.description?.value, - ...(unique ? { unique } : {}), }; if (aliasDirective) { @@ -290,7 +280,6 @@ function getObjFieldMeta({ res.relationFields.push(relationField); res.connectionFields.push(connectionField); - // } } else if (cypherMeta) { const cypherField: CypherField = { ...baseField, @@ -512,8 +501,6 @@ function getObjFieldMeta({ ) as ObjectFields; } -export default getObjFieldMeta; - function parseSelectableDirective(directive: DirectiveNode | undefined): SelectableOptions { const defaultArguments = { onRead: true, diff --git a/packages/graphql/src/schema/get-relationship-meta.test.ts b/packages/graphql/src/schema/get-relationship-meta.test.ts index 5702fbecf6..d6234a1f73 100644 --- a/packages/graphql/src/schema/get-relationship-meta.test.ts +++ b/packages/graphql/src/schema/get-relationship-meta.test.ts @@ -377,7 +377,7 @@ describe("getRelationshipMeta", () => { { // @ts-ignore name: { value: "queryDirection" }, - value: { kind: Kind.ENUM, value: "DEFAULT_UNDIRECTED" }, + value: { kind: Kind.ENUM, value: "UNDIRECTED" }, }, ], }, @@ -389,7 +389,7 @@ describe("getRelationshipMeta", () => { expect(result).toMatchObject({ type: "ACTED_IN", direction: "IN", - queryDirection: "DEFAULT_UNDIRECTED", + queryDirection: "UNDIRECTED", }); }); @@ -592,7 +592,7 @@ describe("getRelationshipMeta", () => { { // @ts-ignore name: { value: "queryDirection" }, - value: { kind: Kind.ENUM, value: "DEFAULT_UNDIRECTED" }, + value: { kind: Kind.ENUM, value: "UNDIRECTED" }, }, { // @ts-ignore @@ -622,7 +622,7 @@ describe("getRelationshipMeta", () => { type: "ACTED_IN", direction: "IN", properties: "ActedIn", - queryDirection: "DEFAULT_UNDIRECTED", + queryDirection: "UNDIRECTED", nestedOperations: ["CONNECT", "CREATE"], }); }); diff --git a/packages/graphql/src/schema/get-where-fields.ts b/packages/graphql/src/schema/get-where-fields.ts index 15d1043fcd..8aca78cf0a 100644 --- a/packages/graphql/src/schema/get-where-fields.ts +++ b/packages/graphql/src/schema/get-where-fields.ts @@ -18,79 +18,34 @@ */ import type { DirectiveNode } from "graphql"; -import type { Directive } from "graphql-compose"; +import type { Directive, InputTypeComposerFieldConfigMapDefinition, SchemaComposer } from "graphql-compose"; import { DEPRECATED } from "../constants"; import type { AttributeAdapter } from "../schema-model/attribute/model-adapters/AttributeAdapter"; import { ConcreteEntityAdapter } from "../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { Neo4jFeaturesSettings } from "../types"; -import { DEPRECATE_IMPLICIT_EQUAL_FILTERS } from "./constants"; +import { fieldConfigsToFieldConfigMap, getRelationshipFilters } from "./generation/augment-where-input"; +import { getInputFilterFromAttributeType } from "./generation/get-input-filter-from-attribute-type"; import { shouldAddDeprecatedFields } from "./generation/utils"; import { graphqlDirectivesToCompose } from "./to-compose"; -function addCypherListFieldFilters({ - field, - type, - result, - deprecatedDirectives, -}: { - field: AttributeAdapter; - type: string; - result: Record< - string, - { - type: string; - directives: Directive[]; - } - >; - deprecatedDirectives: Directive[]; -}) { - result[`${field.name}_ALL`] = { - type, - directives: deprecatedDirectives, - }; - - result[`${field.name}_NONE`] = { - type, - directives: deprecatedDirectives, - }; - - result[`${field.name}_SINGLE`] = { - type, - directives: deprecatedDirectives, - }; - - result[`${field.name}_SOME`] = { - type, - directives: deprecatedDirectives, - }; -} - // TODO: refactoring needed! // isWhereField, isFilterable, ... extracted out into attributes category +// even more now as Cypher filters and generic input object are added in the mix export function getWhereFieldsForAttributes({ attributes, userDefinedFieldDirectives, features, ignoreCypherFieldFilters, + composer, }: { attributes: AttributeAdapter[]; userDefinedFieldDirectives?: Map; features: Neo4jFeaturesSettings | undefined; ignoreCypherFieldFilters: boolean; -}): Record< - string, - { - type: string; - directives: Directive[]; - } -> { - const result: Record< - string, - { - type: string; - directives: Directive[]; - } - > = {}; + composer: SchemaComposer; +}): InputTypeComposerFieldConfigMapDefinition { + const result: InputTypeComposerFieldConfigMapDefinition = {}; + // Add the where fields for each attribute for (const field of attributes) { const userDefinedDirectivesOnField = userDefinedFieldDirectives?.get(field.name); @@ -113,35 +68,38 @@ export function getWhereFieldsForAttributes({ const targetEntityAdapter = new ConcreteEntityAdapter(field.annotations.cypher.targetEntity); const type = targetEntityAdapter.operations.whereInputTypeName; - // Always add base where field filter (e.g. name) - result[field.name] = { - type, - directives: deprecatedDirectives, - }; - // Add list where field filters (e.g. name_ALL, name_NONE, name_SINGLE, name_SOME) if (field.typeHelper.isList()) { - addCypherListFieldFilters({ + addCypherRelationshipLegacyFilters({ field, type, result, deprecatedDirectives, }); + + addCypherRelationshipFilter({ field, type, result, deprecatedDirectives, composer }); + } else { + // Add base where field filter (e.g. name) + result[field.name] = { + type, + directives: deprecatedDirectives, + }; } continue; } } - if (shouldAddDeprecatedFields(features, "implicitEqualFilters")) { - result[field.name] = { - type: field.getInputTypeNames().where.pretty, - directives: deprecatedDirectives.length ? deprecatedDirectives : [DEPRECATE_IMPLICIT_EQUAL_FILTERS], - }; + result[field.name] = { + type: getInputFilterFromAttributeType(field, features), + directives: deprecatedDirectives, + }; + if (!shouldAddDeprecatedFields(features, "attributeFilters")) { + continue; } result[`${field.name}_EQ`] = { type: field.getInputTypeNames().where.pretty, - directives: deprecatedDirectives, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "EQ"), }; // If the field is a boolean, skip it @@ -155,7 +113,7 @@ export function getWhereFieldsForAttributes({ if (field.typeHelper.isList()) { result[`${field.name}_INCLUDES`] = { type: field.getInputTypeNames().where.type, - directives: deprecatedDirectives, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "INCLUDES"), }; continue; @@ -164,15 +122,15 @@ export function getWhereFieldsForAttributes({ // If the field is not an array, add the in and not in fields result[`${field.name}_IN`] = { type: field.getFilterableInputTypeName(), - directives: deprecatedDirectives, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "IN"), }; // If the field is a number or temporal, add the comparison operators if (field.isNumericalOrTemporal()) { - ["_LT", "_LTE", "_GT", "_GTE"].forEach((comparator) => { - result[`${field.name}${comparator}`] = { + ["LT", "LTE", "GT", "GTE"].forEach((comparator) => { + result[`${field.name}_${comparator}`] = { type: field.getInputTypeNames().where.type, - directives: deprecatedDirectives, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator), }; }); continue; @@ -180,10 +138,10 @@ export function getWhereFieldsForAttributes({ // If the field is spatial, add the point comparison operators if (field.typeHelper.isSpatial()) { - ["_DISTANCE", "_LT", "_LTE", "_GT", "_GTE"].forEach((comparator) => { - result[`${field.name}${comparator}`] = { + ["DISTANCE", "LT", "LTE", "GT", "GTE"].forEach((comparator) => { + result[`${field.name}_${comparator}`] = { type: `${field.getTypeName()}Distance`, - directives: deprecatedDirectives, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator), }; }); continue; @@ -192,19 +150,19 @@ export function getWhereFieldsForAttributes({ // If the field is a string, add the string comparison operators if (field.typeHelper.isString() || field.typeHelper.isID()) { const stringWhereOperators: Array<{ comparator: string; typeName: string }> = [ - { comparator: "_CONTAINS", typeName: field.getInputTypeNames().where.type }, - { comparator: "_STARTS_WITH", typeName: field.getInputTypeNames().where.type }, - { comparator: "_ENDS_WITH", typeName: field.getInputTypeNames().where.type }, + { comparator: "CONTAINS", typeName: field.getInputTypeNames().where.type }, + { comparator: "STARTS_WITH", typeName: field.getInputTypeNames().where.type }, + { comparator: "ENDS_WITH", typeName: field.getInputTypeNames().where.type }, ]; Object.entries(features?.filters?.[field.getInputTypeNames().where.type] || {}).forEach( ([filter, enabled]) => { if (enabled) { if (filter === "MATCHES") { - stringWhereOperators.push({ comparator: `_${filter}`, typeName: "String" }); + stringWhereOperators.push({ comparator: filter, typeName: "String" }); } else { stringWhereOperators.push({ - comparator: `_${filter}`, + comparator: filter, typeName: field.getInputTypeNames().where.type, }); } @@ -212,10 +170,125 @@ export function getWhereFieldsForAttributes({ } ); stringWhereOperators.forEach(({ comparator, typeName }) => { - result[`${field.name}${comparator}`] = { type: typeName, directives: deprecatedDirectives }; + result[`${field.name}_${comparator}`] = { + type: typeName, + directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator), + }; }); } } return result; } + +function getAttributeDeprecationDirective( + deprecatedDirectives: Directive[], + field: AttributeAdapter, + comparator: string +): Directive[] { + if (deprecatedDirectives.length) { + return deprecatedDirectives; + } + switch (comparator) { + case "DISTANCE": + case "LT": + case "LTE": + case "GT": + case "GTE": + case "CONTAINS": + case "MATCHES": + case "IN": + case "INCLUDES": + case "EQ": { + return [ + { + name: DEPRECATED, + args: { + reason: `Please use the relevant generic filter ${field.name}: { ${comparator.toLowerCase()}: ... }`, + }, + }, + ]; + } + case "STARTS_WITH": { + return [ + { + name: DEPRECATED, + args: { + reason: `Please use the relevant generic filter ${field.name}: { startsWith: ... }`, + }, + }, + ]; + } + case "ENDS_WITH": { + return [ + { + name: DEPRECATED, + args: { + reason: `Please use the relevant generic filter ${field.name}: { endsWith: ... }`, + }, + }, + ]; + } + default: { + throw new Error(`Unknown comparator: ${comparator}`); + } + } +} + +function addCypherRelationshipFilter({ + field, + type, + result, + deprecatedDirectives, + composer, +}: { + field: AttributeAdapter; + type: string; + result: InputTypeComposerFieldConfigMapDefinition; + deprecatedDirectives: Directive[]; + composer: SchemaComposer; +}) { + const targetName = field.annotations.cypher?.targetEntity?.name; + if (!targetName) { + throw new Error("Target entity is not defined for the cypher field"); + } + + // Relationship filters + const relationshipFiltersFields = fieldConfigsToFieldConfigMap({ + deprecatedDirectives: [], + fields: getRelationshipFilters({ + relationshipInfo: { targetName, inputTypeName: type }, + }), + }); + // this mimic the adapter RelationshipOperation field "relationshipFiltersTypeName" + const relationshipType = `${targetName}RelationshipFilters`; + + composer.getOrCreateITC(relationshipType, (itc) => { + itc.addFields(relationshipFiltersFields); + }); + + result[field.name] = { + type: relationshipType, + directives: deprecatedDirectives, + }; +} + +function addCypherRelationshipLegacyFilters({ + field, + type, + result, + deprecatedDirectives, +}: { + field: AttributeAdapter; + type: string; + result: InputTypeComposerFieldConfigMapDefinition; + deprecatedDirectives: Directive[]; +}) { + const quantifiers = ["ALL", "NONE", "SINGLE", "SOME"] as const; + for (const quantifier of quantifiers) { + result[`${field.name}_${quantifier}`] = { + type, + directives: deprecatedDirectives, + }; + } +} diff --git a/packages/graphql/src/schema/make-augmented-schema.test.ts b/packages/graphql/src/schema/make-augmented-schema.test.ts index d174bff8cd..61f7793628 100644 --- a/packages/graphql/src/schema/make-augmented-schema.test.ts +++ b/packages/graphql/src/schema/make-augmented-schema.test.ts @@ -32,6 +32,7 @@ import { gql } from "graphql-tag"; import { Node } from "../classes"; import { generateModel } from "../schema-model/generate-model"; import makeAugmentedSchema from "./make-augmented-schema"; +import { ComplexityEstimatorHelper } from "../classes/ComplexityEstimatorHelper"; describe("makeAugmentedSchema", () => { test("should be a function", () => { @@ -52,7 +53,7 @@ describe("makeAugmentedSchema", () => { `; const schemaModel = generateModel(mergeTypeDefs(typeDefs)); - const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel }); + const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel, complexityEstimatorHelper: new ComplexityEstimatorHelper(false) }); const document = neoSchema.typeDefs; const queryObject = document.definitions.find( (x) => x.kind === Kind.OBJECT_TYPE_DEFINITION && x.name.value === "Query" @@ -73,12 +74,6 @@ describe("makeAugmentedSchema", () => { ).type as NamedTypeNode; expect(nodeFindQueryType.name.value).toEqual(type); - // Options - const options = document.definitions.find( - (x) => x.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && x.name.value === `${type}Options` - ); - expect(options).toBeTruthy(); - // Where const where = document.definitions.find( (x) => x.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && x.name.value === `${type}Where` @@ -102,7 +97,7 @@ describe("makeAugmentedSchema", () => { `; const schemaModel = generateModel(mergeTypeDefs(typeDefs)); - const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel }); + const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel, complexityEstimatorHelper: new ComplexityEstimatorHelper(false) }); const document = neoSchema.typeDefs; @@ -133,6 +128,7 @@ describe("makeAugmentedSchema", () => { }, }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const document = neoSchema.typeDefs; @@ -165,6 +161,7 @@ describe("makeAugmentedSchema", () => { }, }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const document = neoSchema.typeDefs; @@ -200,6 +197,7 @@ describe("makeAugmentedSchema", () => { }, }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const document = neoSchema.typeDefs; @@ -239,6 +237,7 @@ describe("makeAugmentedSchema", () => { }, }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const document = neoSchema.typeDefs; @@ -272,7 +271,7 @@ describe("makeAugmentedSchema", () => { `; const schemaModel = generateModel(mergeTypeDefs(typeDefs)); - const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel }); + const neoSchema = makeAugmentedSchema({ document: typeDefs, schemaModel, complexityEstimatorHelper: new ComplexityEstimatorHelper(false) }); const document = neoSchema.typeDefs; @@ -292,7 +291,7 @@ describe("makeAugmentedSchema", () => { `; const schemaModel = generateModel(mergeTypeDefs(typeDefs)); - expect(() => makeAugmentedSchema({ document: typeDefs, schemaModel })).not.toThrow( + expect(() => makeAugmentedSchema({ document: typeDefs, schemaModel, complexityEstimatorHelper: new ComplexityEstimatorHelper(false) })).not.toThrow( 'Error: Type with name "ActionMapping" does not exists' ); }); diff --git a/packages/graphql/src/schema/make-augmented-schema.ts b/packages/graphql/src/schema/make-augmented-schema.ts index 974c140bdf..0a211c5d2d 100644 --- a/packages/graphql/src/schema/make-augmented-schema.ts +++ b/packages/graphql/src/schema/make-augmented-schema.ts @@ -39,9 +39,8 @@ import { AggregationTypesMapper } from "./aggregations/aggregation-types-mapper" import { augmentFulltextSchema } from "./augment/fulltext"; import { ensureNonEmptyInput } from "./ensure-non-empty-input"; import getCustomResolvers from "./get-custom-resolvers"; -import { getDefinitionNodes } from "./get-definition-nodes"; import type { ObjectFields } from "./get-obj-field-meta"; -import getObjFieldMeta from "./get-obj-field-meta"; +import { getObjFieldMeta } from "./get-obj-field-meta"; import { cypherResolver } from "./resolvers/field/cypher"; import { createResolver } from "./resolvers/mutation/create"; import { deleteResolver } from "./resolvers/mutation/delete"; @@ -68,9 +67,10 @@ import { UnionEntity } from "../schema-model/entity/UnionEntity"; import { ConcreteEntityAdapter } from "../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import { InterfaceEntityAdapter } from "../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import { UnionEntityAdapter } from "../schema-model/entity/model-adapters/UnionEntityAdapter"; +import { getDefinitionCollection } from "../schema-model/parser/definition-collection"; import { RelationshipDeclarationAdapter } from "../schema-model/relationship/model-adapters/RelationshipDeclarationAdapter"; import type { CypherField, Neo4jFeaturesSettings } from "../types"; -import { filterTruthy } from "../utils/utils"; +import { asArray, filterTruthy } from "../utils/utils"; import { augmentVectorSchema } from "./augment/vector"; import { createConnectionFields } from "./create-connection-fields"; import { addGlobalNodeFields } from "./create-global-nodes"; @@ -81,7 +81,6 @@ import { withCreateInputType } from "./generation/create-input"; import { withInterfaceType } from "./generation/interface-type"; import { withObjectType } from "./generation/object-type"; import { withMutationResponseTypes } from "./generation/response-types"; -import { withOptionsInputType } from "./generation/sort-and-options-input"; import { withUpdateInputType } from "./generation/update-input"; import { withUniqueWhereInputType, withWhereInputType } from "./generation/where-input"; import getNodes from "./get-nodes"; @@ -89,6 +88,7 @@ import { getResolveAndSubscriptionMethods } from "./get-resolve-and-subscription import { filterInterfaceTypes } from "./make-augmented-schema/filter-interface-types"; import { getUserDefinedDirectives } from "./make-augmented-schema/user-defined-directives"; import { generateSubscriptionTypes } from "./subscriptions/generate-subscription-types"; +import { type ComplexityEstimatorHelper } from "../classes/ComplexityEstimatorHelper"; function definitionNodeHasName(x: DefinitionNode): x is DefinitionNode & { name: NameNode } { return "name" in x; @@ -100,12 +100,14 @@ function makeAugmentedSchema({ userCustomResolvers, subgraph, schemaModel, + complexityEstimatorHelper, }: { document: DocumentNode; features?: Neo4jFeaturesSettings; userCustomResolvers?: IResolvers | Array; subgraph?: Subgraph; schemaModel: Neo4jGraphQLSchemaModel; + complexityEstimatorHelper: ComplexityEstimatorHelper; }): { nodes: Node[]; relationships: Relationship[]; @@ -116,15 +118,26 @@ function makeAugmentedSchema({ const callbacks = features?.populatedBy?.callbacks; let relationships: Relationship[] = []; + //TODO: definition collection is being used to harmonize schema generation with schema model, + //however make augmented schema inner methods are still accepting arrays as they were defined by the previous getDefinitionNodes + const definitionCollection = getDefinitionCollection(document); + + const { + interfaceTypes, + scalarTypes, + userDefinedObjectTypes, + enumTypes, + unionTypes, + inputTypes, + directives, + schemaExtensions, + } = definitionCollection; - const definitionNodes = getDefinitionNodes(document); const customResolvers = getCustomResolvers(document); - const { interfaceTypes, scalarTypes, objectTypes, enumTypes, unionTypes, schemaExtensions } = definitionNodes; - // TODO: maybe use schemaModel.definitionCollection instead of definitionNodes? need to add inputObjectTypes and customResolvers const schemaGenerator = new AugmentedSchemaGenerator( schemaModel, - definitionNodes, + definitionCollection, [customResolvers.customQuery, customResolvers.customMutation, customResolvers.customSubscription].filter( (x): x is ObjectTypeDefinitionNode => Boolean(x) ) @@ -132,19 +145,115 @@ function makeAugmentedSchema({ const generatorComposer = schemaGenerator.generate(); composer.merge(generatorComposer); + // Generates the filters for enums, which are reused + Array.from(enumTypes.values()).forEach((enumType) => { + composer.createInputTC({ + name: `${enumType.name.value}EnumScalarFilters`, + description: `${enumType.name.value} filters`, + fields: { + eq: { + type: enumType.name.value, + }, + in: { type: `[${enumType.name.value}!]` }, + }, + }); + composer.createInputTC({ + name: `${enumType.name.value}ListEnumScalarFilters`, + description: `${enumType.name.value} filters`, + fields: { + eq: { + type: `[${enumType.name.value}!]`, + }, + includes: { + type: enumType.name.value, + }, + }, + }); + }); + + // Generates the mutations for enums, which are reused + Array.from(enumTypes.values()).forEach((enumType) => { + composer.createInputTC({ + name: `${enumType.name.value}EnumScalarMutations`, + description: `${enumType.name.value} mutations`, + fields: { + set: { type: enumType.name.value }, + }, + }); + composer.createInputTC({ + name: `${enumType.name.value}ListEnumScalarMutations`, + description: `Mutations for a list for ${enumType.name.value}`, + fields: { + set: { type: `[${enumType.name.value}!]!` }, + push: { type: `[${enumType.name.value}!]!` }, + pop: { type: enumType.name.value }, + }, + }); + }); + + // Generates the filters for custom scalars + Array.from(scalarTypes.values()).forEach((enumType) => { + composer.createInputTC({ + name: `${enumType.name.value}ScalarFilters`, + description: `${enumType.name.value} filters`, + fields: { + eq: { + type: enumType.name.value, + }, + in: { type: `[${enumType.name.value}!]` }, + }, + }); + + composer.createInputTC({ + name: `${enumType.name.value}ListScalarFilters`, + description: `${enumType.name.value} filters`, + fields: { + eq: { + type: `[${enumType.name.value}!]`, + }, + includes: { + type: enumType.name.value, + }, + }, + }); + }); + + // Generates the mutations for custom scalars + Array.from(scalarTypes.values()).forEach((enumType) => { + composer.createInputTC({ + name: `${enumType.name.value}ScalarMutations`, + description: `${enumType.name.value} filters`, + fields: { + set: { type: enumType.name.value }, + }, + }); + + composer.createInputTC({ + name: `${enumType.name.value}ListScalarMutations`, + description: `Mutations for a list for ${enumType.name.value}`, + fields: { + set: { type: `[${enumType.name.value}!]!` }, + push: { type: `[${enumType.name.value}!]!` }, + pop: { type: enumType.name.value }, + }, + }); + }); + // TODO: move these to SchemaGenerator once the other types are moved (in the meantime references to object types are causing errors because they are not present in the generated schema) const pipedDefs = [ - ...definitionNodes.enumTypes, - ...definitionNodes.scalarTypes, - ...definitionNodes.inputObjectTypes, - ...definitionNodes.unionTypes, - ...definitionNodes.directives, + ...userDefinedObjectTypes.values(), + ...enumTypes.values(), + ...scalarTypes.values(), + ...inputTypes.values(), + ...unionTypes.values(), + ...directives.values(), ...filterTruthy([ customResolvers.customQuery, customResolvers.customMutation, customResolvers.customSubscription, ]), ]; + if (pipedDefs.length) { composer.addTypeDefs(print({ kind: Kind.DOCUMENT, definitions: pipedDefs })); } @@ -160,17 +269,13 @@ function makeAugmentedSchema({ const aggregationTypesMapper = new AggregationTypesMapper(composer, subgraph); - const getNodesResult = getNodes(definitionNodes, { callbacks, userCustomResolvers }); + const getNodesResult = getNodes(definitionCollection, { callbacks, userCustomResolvers }); - const { nodes, relationshipPropertyInterfaceNames, interfaceRelationshipNames } = getNodesResult; + const { nodes, interfaceRelationshipNames } = getNodesResult; const hasGlobalNodes = addGlobalNodeFields(nodes, composer, schemaModel.concreteEntities); - const { filteredInterfaceTypes } = filterInterfaceTypes(interfaceTypes, interfaceRelationshipNames); - - const relationshipProperties: ObjectTypeDefinitionNode[] = objectTypes.filter((objectType) => { - return relationshipPropertyInterfaceNames.has(objectType.name.value); - }); + const { filteredInterfaceTypes } = filterInterfaceTypes(interfaceTypes.values(), interfaceRelationshipNames); const { userDefinedFieldDirectivesForNode, @@ -178,7 +283,7 @@ function makeAugmentedSchema({ propagatedDirectivesForNode, userDefinedDirectivesForInterface, userDefinedDirectivesForUnion, - } = getUserDefinedDirectives(definitionNodes); + } = getUserDefinedDirectives(definitionCollection); /** * TODO [translation-layer-compatibility] @@ -186,19 +291,16 @@ function makeAugmentedSchema({ * actual functional logic is in schemaModel.concreteEntities.forEach */ const relationshipFields = new Map(); - relationshipProperties.forEach((relationship) => { + for (const relationship of definitionCollection.relationshipProperties.values()) { const relFields = getObjFieldMeta({ - enums: enumTypes, interfaces: filteredInterfaceTypes, - objects: objectTypes, - scalars: scalarTypes, - unions: unionTypes, + definitionCollection, obj: relationship, callbacks, }); relationshipFields.set(relationship.name.value, relFields); - }); + } // this is the new "functional" way for the above forEach // helper to only create relationshipProperties Interface types once, even if multiple relationships reference it @@ -218,11 +320,12 @@ function makeAugmentedSchema({ graphqlDirectivesToCompose(userDefinedDirectivesForUnion.get(unionEntityAdapter.name) || []) ); if (unionEntityAdapter.isReadable) { + complexityEstimatorHelper.registerField("Query", unionEntityAdapter.operations.rootTypeFieldNames.read) composer.Query.addFields({ [unionEntityAdapter.operations.rootTypeFieldNames.read]: findResolver({ entityAdapter: unionEntityAdapter, - features, composer, + isLimitRequired: features?.limitRequired, }), }); } @@ -241,6 +344,7 @@ function makeAugmentedSchema({ aggregationTypesMapper, seenRelationshipPropertiesTypes, features, + complexityEstimatorHelper, }); const connectionFields = createConnectionFields({ entityAdapter: interfaceEntityAdapter, @@ -258,6 +362,7 @@ function makeAugmentedSchema({ if (!node) { throw new Error(`Node not found with the name ${entity.name}`); } + const concreteEntityAdapter = new ConcreteEntityAdapter(entity); const userDefinedFieldDirectives = userDefinedFieldDirectivesForNode.get(concreteEntityAdapter.name); if (!userDefinedFieldDirectives) { @@ -281,6 +386,7 @@ function makeAugmentedSchema({ seenRelationshipPropertiesTypes, userDefinedDirectivesForNode, userDefinedFieldDirectivesForNode, + complexityEstimatorHelper, }); const connectionFields = createConnectionFields({ @@ -318,11 +424,8 @@ function makeAugmentedSchema({ */ const objectFields = getObjFieldMeta({ obj: customResolvers[`customCypher${type}`], - scalars: scalarTypes, - enums: enumTypes, interfaces: filteredInterfaceTypes, - unions: unionTypes, - objects: objectTypes, + definitionCollection, callbacks, }); const field = objectFields.cypherFields.find((f) => f.fieldName === attributeAdapter.name) as CypherField; @@ -358,9 +461,11 @@ function makeAugmentedSchema({ } const generatedTypeDefs = composer.toSDL(); + let parsedDoc = parse(generatedTypeDefs); - + const documentNames = new Set(parsedDoc.definitions.filter(definitionNodeHasName).map((x) => x.name.value)); + const resolveMethods = getResolveAndSubscriptionMethods(composer); const generatedResolveMethods: GraphQLToolsResolveMethods = {}; @@ -419,55 +524,10 @@ function makeAugmentedSchema({ } }); - // do not propagate Neo4jGraphQL directives on schema extensions - const schemaExtensionsWithoutNeo4jDirectives = schemaExtensions.map((schemaExtension): SchemaExtensionNode => { - return { - kind: schemaExtension.kind, - loc: schemaExtension.loc, - operationTypes: schemaExtension.operationTypes, - directives: schemaExtension.directives?.filter( - (schemaDirective) => - !["query", "mutation", "subscription", "authentication"].includes(schemaDirective.name.value) - ), - }; - }); - const seen = {}; parsedDoc = { ...parsedDoc, - definitions: [ - ...parsedDoc.definitions.filter((definition) => { - // Filter out default scalars, they are not needed and can cause issues - if (definition.kind === Kind.SCALAR_TYPE_DEFINITION) { - if ( - [ - GraphQLBoolean.name, - GraphQLFloat.name, - GraphQLID.name, - GraphQLInt.name, - GraphQLString.name, - ].includes(definition.name.value) - ) { - return false; - } - } - - if (!("name" in definition)) { - return true; - } - - const n = definition.name?.value as string; - - if (seen[n]) { - return false; - } - - seen[n] = n; - - return true; - }), - ...schemaExtensionsWithoutNeo4jDirectives, - ], - }; + definitions: getTransformedDefinitionNodesForAugmentedSchema({schemaExtensions, definitions: parsedDoc.definitions, complexityEstimatorHelper}), + } return { nodes, @@ -477,6 +537,68 @@ function makeAugmentedSchema({ }; } +function getTransformedDefinitionNodesForAugmentedSchema({ + schemaExtensions, + definitions, + complexityEstimatorHelper, +}: { + schemaExtensions: SchemaExtensionNode | undefined; + definitions: readonly DefinitionNode[]; + complexityEstimatorHelper: ComplexityEstimatorHelper +}): DefinitionNode[] { + const definitionNodes: DefinitionNode[] = [] + // do not propagate Neo4jGraphQL directives on schema extensions + asArray(schemaExtensions).reduce( + (acc, schemaExtension: SchemaExtensionNode) => { + acc.push({ + kind: schemaExtension.kind, + loc: schemaExtension.loc, + operationTypes: schemaExtension.operationTypes, + directives: schemaExtension.directives?.filter( + (schemaDirective) => + !["query", "mutation", "subscription", "authentication"].includes(schemaDirective.name.value) + ), + }) + return acc; + }, definitionNodes) + // filter out some definition nodes + // add FieldEstimator extensions for complexity calculation + const seen = {} + definitions.reduce((acc, definition) => { + if (shouldKeepDefinitionNode(definition, seen)) { + acc.push(complexityEstimatorHelper.hydrateDefinitionNodeWithComplexityExtensions(definition)) + } + return acc; + }, definitionNodes) + return definitionNodes; +} + +function shouldKeepDefinitionNode(definition: DefinitionNode, seen: Record) { + // Filter out default scalars, they are not needed and can cause issues + if (definition.kind === Kind.SCALAR_TYPE_DEFINITION) { + if ( + [ + GraphQLBoolean.name, + GraphQLFloat.name, + GraphQLID.name, + GraphQLInt.name, + GraphQLString.name, + ].includes(definition.name.value) + ) { + return false; + } + } + if (!("name" in definition)) { + return true; + } + const n = definition.name?.value as string; + if (seen[n]) { + return false; + } + seen[n] = n; + return true; +} + export default makeAugmentedSchema; // TODO: unify object & interface fns @@ -494,6 +616,7 @@ function generateObjectType({ seenRelationshipPropertiesTypes, userDefinedDirectivesForNode, userDefinedFieldDirectivesForNode, + complexityEstimatorHelper, }: { composer: SchemaComposer; concreteEntityAdapter: ConcreteEntityAdapter; @@ -507,20 +630,16 @@ function generateObjectType({ seenRelationshipPropertiesTypes: Set; userDefinedDirectivesForNode: Map; userDefinedFieldDirectivesForNode: Map>; + complexityEstimatorHelper: ComplexityEstimatorHelper; }) { - withOptionsInputType({ entityAdapter: concreteEntityAdapter, userDefinedFieldDirectives, composer }); withWhereInputType({ entityAdapter: concreteEntityAdapter, userDefinedFieldDirectives, features, composer, }); - /** - * TODO [translation-layer-compatibility] - * Need to migrate resolvers, which themselves rely on the translation layer being migrated to the new schema model - */ - augmentFulltextSchema(node, composer, concreteEntityAdapter); - augmentVectorSchema({ composer, concreteEntityAdapter, features }); + augmentFulltextSchema({ composer, concreteEntityAdapter, features, complexityEstimatorHelper }); + augmentVectorSchema({ composer, concreteEntityAdapter, features, complexityEstimatorHelper }); withUniqueWhereInputType({ concreteEntityAdapter, composer }); withCreateInputType({ entityAdapter: concreteEntityAdapter, userDefinedFieldDirectives, composer }); withUpdateInputType({ entityAdapter: concreteEntityAdapter, userDefinedFieldDirectives, composer, features }); @@ -541,17 +660,19 @@ function generateObjectType({ userDefinedFieldDirectivesForNode, features, seenRelationshipPropertiesTypes, + complexityEstimatorHelper, }); ensureNonEmptyInput(composer, concreteEntityAdapter.operations.updateInputTypeName); ensureNonEmptyInput(composer, concreteEntityAdapter.operations.createInputTypeName); if (concreteEntityAdapter.isReadable) { + complexityEstimatorHelper.registerField("Query", concreteEntityAdapter.operations.rootTypeFieldNames.read) composer.Query.addFields({ [concreteEntityAdapter.operations.rootTypeFieldNames.read]: findResolver({ entityAdapter: concreteEntityAdapter, - features, composer, + isLimitRequired: features?.limitRequired, }), }); composer.Query.setFieldDirectives( @@ -559,11 +680,14 @@ function generateObjectType({ graphqlDirectivesToCompose(propagatedDirectives) ); + + complexityEstimatorHelper.registerField("Query", concreteEntityAdapter.operations.rootTypeFieldNames.connection) composer.Query.addFields({ [concreteEntityAdapter.operations.rootTypeFieldNames.connection]: rootConnectionResolver({ composer, entityAdapter: concreteEntityAdapter, propagatedDirectives, + isLimitRequired: features?.limitRequired, }), }); composer.Query.setFieldDirectives( @@ -641,6 +765,7 @@ function generateInterfaceObjectType({ propagatedDirectivesForNode, aggregationTypesMapper, seenRelationshipPropertiesTypes, + complexityEstimatorHelper, }: { composer: SchemaComposer; interfaceEntityAdapter: InterfaceEntityAdapter; @@ -651,12 +776,12 @@ function generateInterfaceObjectType({ propagatedDirectivesForNode: Map; aggregationTypesMapper: AggregationTypesMapper; seenRelationshipPropertiesTypes: Set; + complexityEstimatorHelper: ComplexityEstimatorHelper; }) { const userDefinedFieldDirectives = userDefinedFieldDirectivesForNode.get(interfaceEntityAdapter.name) as Map< string, DirectiveNode[] >; - withOptionsInputType({ entityAdapter: interfaceEntityAdapter, userDefinedFieldDirectives, composer }); withWhereInputType({ entityAdapter: interfaceEntityAdapter, userDefinedFieldDirectives, @@ -682,15 +807,17 @@ function generateInterfaceObjectType({ userDefinedFieldDirectivesForNode, features, seenRelationshipPropertiesTypes, + complexityEstimatorHelper, }); const propagatedDirectives = propagatedDirectivesForNode.get(interfaceEntityAdapter.name) || []; if (interfaceEntityAdapter.isReadable) { + complexityEstimatorHelper.registerField("Query", interfaceEntityAdapter.operations.rootTypeFieldNames.read) composer.Query.addFields({ [interfaceEntityAdapter.operations.rootTypeFieldNames.read]: findResolver({ entityAdapter: interfaceEntityAdapter, - features, composer, + isLimitRequired: features?.limitRequired, }), }); @@ -699,11 +826,13 @@ function generateInterfaceObjectType({ graphqlDirectivesToCompose(propagatedDirectives) ); + complexityEstimatorHelper.registerField("Query", interfaceEntityAdapter.operations.rootTypeFieldNames.connection) composer.Query.addFields({ [interfaceEntityAdapter.operations.rootTypeFieldNames.connection]: rootConnectionResolver({ composer, entityAdapter: interfaceEntityAdapter, propagatedDirectives, + isLimitRequired: features?.limitRequired, }), }); composer.Query.setFieldDirectives( diff --git a/packages/graphql/src/schema/make-augmented-schema/filter-interface-types.ts b/packages/graphql/src/schema/make-augmented-schema/filter-interface-types.ts index 2ad403556c..c75b0fddd0 100644 --- a/packages/graphql/src/schema/make-augmented-schema/filter-interface-types.ts +++ b/packages/graphql/src/schema/make-augmented-schema/filter-interface-types.ts @@ -20,7 +20,7 @@ import type { InterfaceTypeDefinitionNode } from "graphql"; export function filterInterfaceTypes( - interfaceTypes: InterfaceTypeDefinitionNode[], + interfaceTypes: Iterable, interfaceRelationshipNames: Set ): { interfaceRelationships: InterfaceTypeDefinitionNode[]; diff --git a/packages/graphql/src/schema/make-augmented-schema/user-defined-directives.ts b/packages/graphql/src/schema/make-augmented-schema/user-defined-directives.ts index 05aad3fa80..8ad178d666 100644 --- a/packages/graphql/src/schema/make-augmented-schema/user-defined-directives.ts +++ b/packages/graphql/src/schema/make-augmented-schema/user-defined-directives.ts @@ -20,8 +20,8 @@ import type { DirectiveNode, InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; import { PROPAGATED_DIRECTIVES } from "../../constants"; import { LIBRARY_DIRECTIVES } from "../../schema-model/library-directives"; +import type { DefinitionCollection } from "../../schema-model/parser/definition-collection"; import { isInArray } from "../../utils/is-in-array"; -import type { DefinitionNodes } from "../get-definition-nodes"; function getUserDefinedMergedFieldDirectivesForDefinition( definitionNode: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode @@ -42,14 +42,14 @@ function getUserDefinedMergedFieldDirectivesForDefinition( return userDefinedFieldDirectives; } -export function getUserDefinedDirectives(definitionNodes: DefinitionNodes) { +export function getUserDefinedDirectives(definitionCollection: DefinitionCollection) { const userDefinedFieldDirectivesForNode = new Map>(); const userDefinedDirectivesForNode = new Map(); const propagatedDirectivesForNode = new Map(); const userDefinedDirectivesForInterface = new Map(); const userDefinedDirectivesForUnion = new Map(); - for (const definitionNode of definitionNodes.objectTypes) { + for (const definitionNode of definitionCollection.objectTypes.values()) { const userDefinedObjectDirectives = definitionNode.directives?.filter((directive) => !isInArray(LIBRARY_DIRECTIVES, directive.name.value)) || []; @@ -62,7 +62,7 @@ export function getUserDefinedDirectives(definitionNodes: DefinitionNodes) { userDefinedFieldDirectivesForNode.set(definitionNode.name.value, userDefinedFieldDirectives); } - for (const definitionNode of definitionNodes.interfaceTypes) { + for (const definitionNode of definitionCollection.interfaceTypes.values()) { const userDefinedInterfaceDirectives = definitionNode.directives?.filter((directive) => !isInArray(LIBRARY_DIRECTIVES, directive.name.value)) || []; @@ -75,7 +75,7 @@ export function getUserDefinedDirectives(definitionNodes: DefinitionNodes) { userDefinedFieldDirectivesForNode.set(definitionNode.name.value, userDefinedFieldDirectives); } - for (const definitionNode of definitionNodes.unionTypes) { + for (const definitionNode of definitionCollection.unionTypes.values()) { const userDefinedUnionDirectives = definitionNode.directives?.filter((directive) => !isInArray(LIBRARY_DIRECTIVES, directive.name.value)) || []; @@ -86,7 +86,7 @@ export function getUserDefinedDirectives(definitionNodes: DefinitionNodes) { propagatedDirectivesForNode.set(definitionNode.name.value, propagatedDirectives); } - for (const definitionNode of definitionNodes.operations) { + for (const definitionNode of definitionCollection.operations) { const userDefinedFieldDirectives = getUserDefinedMergedFieldDirectivesForDefinition(definitionNode); userDefinedFieldDirectivesForNode.set(definitionNode.name.value, userDefinedFieldDirectives); } diff --git a/packages/graphql/src/schema/parse/get-unique-meta.ts b/packages/graphql/src/schema/parse/get-unique-meta.ts deleted file mode 100644 index b551cd6f3c..0000000000 --- a/packages/graphql/src/schema/parse/get-unique-meta.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { DirectiveNode, InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { Kind } from "graphql"; -import type { Unique } from "../../types"; - -function getUniqueMeta( - directives: DirectiveNode[], - type: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, - fieldName: string -): Unique | undefined { - const uniqueDirective = directives.find((x) => x.name.value === "unique"); - - if (uniqueDirective && type.kind === Kind.INTERFACE_TYPE_DEFINITION) { - throw new Error(`@unique directive cannot be used on interface type fields: ${type.name.value}.${fieldName}`); - } - - if (uniqueDirective) { - const constraintName = uniqueDirective.arguments?.find((a) => a.name.value === "constraintName"); - if (constraintName && constraintName.value.kind === Kind.STRING) { - return { constraintName: constraintName.value.value }; - } - return { constraintName: `${type.name.value}_${fieldName}` }; - } - - if (directives.some((directive) => directive.name.value === "relayId")) { - return { constraintName: `${type.name.value}_${fieldName}` }; - } -} - -export default getUniqueMeta; diff --git a/packages/graphql/src/schema/parse/parse-fulltext-directive.test.ts b/packages/graphql/src/schema/parse/parse-fulltext-directive.test.ts deleted file mode 100644 index 92933877ba..0000000000 --- a/packages/graphql/src/schema/parse/parse-fulltext-directive.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { DirectiveNode, ObjectTypeDefinitionNode } from "graphql"; -import gql from "graphql-tag"; -import getObjFieldMeta from "../get-obj-field-meta"; -import parseFulltextDirective from "./parse-fulltext-directive"; - -describe("parseFulltextDirective", () => { - test("should throw error when directive has duplicate name", () => { - const typeDefs = gql` - type Movie - @fulltext( - indexes: [{ indexName: "MyIndex", fields: ["title"] }, { indexName: "MyIndex", fields: ["title"] }] - ) { - title: String - description: String - } - `; - - const definition = typeDefs.definitions[0] as unknown as ObjectTypeDefinitionNode; - const directive = (definition.directives || [])[0] as DirectiveNode; - - const nodeFields = getObjFieldMeta({ - obj: definition, - enums: [], - interfaces: [], - scalars: [], - unions: [], - objects: [], - }); - - expect(() => - parseFulltextDirective({ - directive, - definition, - nodeFields, - }) - ).toThrow("Node 'Movie' @fulltext index contains duplicate name 'MyIndex'"); - }); - - test("should throw error when directive field is missing", () => { - const typeDefs = gql` - type Movie @fulltext(indexes: [{ indexName: "MyIndex", fields: ["title"] }]) @node { - description: String - imdbRating: Int - } - `; - - const definition = typeDefs.definitions[0] as unknown as ObjectTypeDefinitionNode; - const directive = (definition.directives || [])[0] as DirectiveNode; - - const nodeFields = getObjFieldMeta({ - obj: definition, - enums: [], - interfaces: [], - scalars: [], - unions: [], - objects: [], - }); - - expect(() => - parseFulltextDirective({ - directive, - definition, - nodeFields, - }) - ).toThrow( - "Node 'Movie' @fulltext index contains invalid index 'MyIndex' cannot use find String or ID field 'title'" - ); - }); - - test("should return valid Fulltext", () => { - const typeDefs = gql` - type Movie - @fulltext( - indexes: [ - { indexName: "MovieTitle", fields: ["title"] } - { indexName: "MovieDescription", fields: ["description"] } - ] - ) { - title: String - description: String - } - `; - - const definition = typeDefs.definitions[0] as unknown as ObjectTypeDefinitionNode; - const directive = (definition.directives || [])[0] as DirectiveNode; - - const nodeFields = getObjFieldMeta({ - obj: definition, - enums: [], - interfaces: [], - scalars: [], - unions: [], - objects: [], - }); - - const result = parseFulltextDirective({ - directive, - definition, - nodeFields, - }); - - expect(result).toEqual({ - indexes: [ - { indexName: "MovieTitle", fields: ["title"] }, - { indexName: "MovieDescription", fields: ["description"] }, - ], - }); - }); -}); diff --git a/packages/graphql/src/schema/parse/parse-fulltext-directive.ts b/packages/graphql/src/schema/parse/parse-fulltext-directive.ts deleted file mode 100644 index 317dc344c3..0000000000 --- a/packages/graphql/src/schema/parse/parse-fulltext-directive.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ArgumentNode, DirectiveNode, ObjectTypeDefinitionNode } from "graphql"; -import type { FullText, FulltextContext } from "../../types"; -import type { ObjectFields } from "../get-obj-field-meta"; -import { parseValueNode } from "../../schema-model/parser/parse-value-node"; - -function parseFulltextDirective({ - directive, - nodeFields, - definition, -}: { - directive: DirectiveNode; - nodeFields: ObjectFields; - definition: ObjectTypeDefinitionNode; -}): FullText { - const indexesArg = directive.arguments?.find((arg) => arg.name.value === "indexes") as ArgumentNode; - const value = parseValueNode(indexesArg.value) as FulltextContext[]; - const compatibleFields = nodeFields.primitiveFields.filter( - (f) => ["String", "ID"].includes(f.typeMeta.name) && !f.typeMeta.array - ); - - value.forEach((index) => { - // TODO: remove indexName assignment and undefined check once the name argument has been removed. - const indexName = index.indexName || index.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - const names = value.filter((i) => indexName === i.indexName || indexName === i.name); - if (names.length > 1) { - throw new Error(`Node '${definition.name.value}' @fulltext index contains duplicate name '${indexName}'`); - } - - index.fields.forEach((field) => { - const foundField = compatibleFields.find((f) => f.fieldName === field); - if (!foundField) { - throw new Error( - `Node '${definition.name.value}' @fulltext index contains invalid index '${indexName}' cannot use find String or ID field '${field}'` - ); - } - }); - }); - - return { indexes: value }; -} - -export default parseFulltextDirective; diff --git a/packages/graphql/src/schema/resolvers/mutation/update.test.ts b/packages/graphql/src/schema/resolvers/mutation/update.test.ts index 9b18c0c0c0..d7899edc37 100644 --- a/packages/graphql/src/schema/resolvers/mutation/update.test.ts +++ b/packages/graphql/src/schema/resolvers/mutation/update.test.ts @@ -75,7 +75,6 @@ describe("Update resolver", () => { create: "MovieRelationInput", delete: "MovieDeleteInput", - connectOrCreate: "MovieConnectOrCreateInput", }); expect(result.args).toMatchObject({ where: "MovieWhere", diff --git a/packages/graphql/src/schema/resolvers/query/aggregate.ts b/packages/graphql/src/schema/resolvers/query/aggregate.ts index 23919637c7..3f0f4f4a0c 100644 --- a/packages/graphql/src/schema/resolvers/query/aggregate.ts +++ b/packages/graphql/src/schema/resolvers/query/aggregate.ts @@ -25,7 +25,6 @@ import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphq import { execute } from "../../../utils"; import getNeo4jResolveTree from "../../../utils/get-neo4j-resolve-tree"; import type { Neo4jGraphQLComposedContext } from "../composition/wrap-query-and-mutation"; -import { isConcreteEntity } from "../../../translate/queryAST/utils/is-concrete-entity"; export function aggregateResolver({ entityAdapter, @@ -58,15 +57,6 @@ export function aggregateResolver({ resolve, args: { where: entityAdapter.operations.whereInputTypeName, - ...(isConcreteEntity(entityAdapter) && entityAdapter.annotations.fulltext - ? { - fulltext: { - type: entityAdapter.operations.fullTextInputTypeName, - description: - "Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score.", - }, - } - : {}), }, }; } diff --git a/packages/graphql/src/schema/resolvers/query/fulltext.ts b/packages/graphql/src/schema/resolvers/query/fulltext.ts index 6184eec08c..28aefb8c11 100644 --- a/packages/graphql/src/schema/resolvers/query/fulltext.ts +++ b/packages/graphql/src/schema/resolvers/query/fulltext.ts @@ -17,25 +17,23 @@ * limitations under the License. */ -import Cypher from "@neo4j/cypher-builder"; -import type { GraphQLFieldResolver, GraphQLResolveInfo } from "graphql"; -import type { Node } from "../../../classes"; +import type { GraphQLFieldResolver, GraphQLResolveInfo, SelectionSetNode } from "graphql"; import type { ConcreteEntityAdapter } from "../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { InterfaceEntityAdapter } from "../../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import { translateRead } from "../../../translate"; import type { FulltextContext } from "../../../types"; -import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { execute } from "../../../utils"; import getNeo4jResolveTree from "../../../utils/get-neo4j-resolve-tree"; +import { isNeoInt } from "../../../utils/utils"; +import { createConnectionWithEdgeProperties } from "../../pagination"; import type { Neo4jGraphQLComposedContext } from "../composition/wrap-query-and-mutation"; +import { emptyConnection } from "./empty-connection"; export function fulltextResolver({ - node, - index, + fulltextContext, entityAdapter, }: { - node: Node; - index: FulltextContext; + fulltextContext: FulltextContext; entityAdapter: ConcreteEntityAdapter | InterfaceEntityAdapter; }): GraphQLFieldResolver { return async function resolve( @@ -44,8 +42,7 @@ export function fulltextResolver({ context: Neo4jGraphQLComposedContext, info: GraphQLResolveInfo ) { - context.fulltext = index; - context.fulltext.scoreVariable = new Cypher.Variable(); + context.fulltext = fulltextContext; const resolveTree = getNeo4jResolveTree(info, { args }); resolveTree.args.options = { @@ -54,12 +51,10 @@ export function fulltextResolver({ offset: resolveTree.args.offset, }; - (context as Neo4jGraphQLTranslationContext).resolveTree = resolveTree; - const { cypher, params } = translateRead({ - context: context as Neo4jGraphQLTranslationContext, + context: { ...context, resolveTree }, entityAdapter, - varName: node.singular, + varName: "this", }); const executeResult = await execute({ cypher, @@ -68,6 +63,24 @@ export function fulltextResolver({ context, info, }); - return executeResult.records; + + if (!executeResult.records[0]) { + return { [entityAdapter.operations.rootTypeFieldNames.connection]: emptyConnection }; + } + + const record = executeResult.records[0].this; + const totalCount = isNeoInt(record.totalCount) ? record.totalCount.toNumber() : record.totalCount; + const connection = createConnectionWithEdgeProperties({ + selectionSet: resolveTree as unknown as SelectionSetNode, + source: { edges: record.edges }, + args: { first: args.first, after: args.after }, + totalCount, + }); + + return { + totalCount, + edges: connection.edges, + pageInfo: connection.pageInfo, + }; }; } diff --git a/packages/graphql/src/schema/resolvers/query/global-node.ts b/packages/graphql/src/schema/resolvers/query/global-node.ts index fac207ab84..47503ec55c 100644 --- a/packages/graphql/src/schema/resolvers/query/global-node.ts +++ b/packages/graphql/src/schema/resolvers/query/global-node.ts @@ -60,7 +60,7 @@ export function globalNodeResolver({ entities }: { entities: ConcreteEntityAdapt const resolveTree = { name: entityAdapter.plural, alias: "node", - args: { where: { [field]: id } }, + args: { where: { [`${field}_EQ`]: id } }, fieldsByTypeName, }; diff --git a/packages/graphql/src/schema/resolvers/query/read.test.ts b/packages/graphql/src/schema/resolvers/query/read.test.ts index 6191e228f5..123983bcf8 100644 --- a/packages/graphql/src/schema/resolvers/query/read.test.ts +++ b/packages/graphql/src/schema/resolvers/query/read.test.ts @@ -35,22 +35,11 @@ describe("Read resolver", () => { }); const concreteEntityAdapter = new ConcreteEntityAdapter(concreteEntity); - const result = findResolver({ entityAdapter: concreteEntityAdapter, composer: new SchemaComposer() }); + const result = findResolver({ entityAdapter: concreteEntityAdapter, composer: new SchemaComposer(), isLimitRequired: undefined }); expect(result.type).toBe(`[Movie!]!`); expect(result.resolve).toBeInstanceOf(Function); expect(result.args).toMatchObject({ where: `MovieWhere`, - options: { - directives: [ - { - args: { - reason: "Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.", - }, - name: "deprecated", - }, - ], - type: "MovieOptions", - }, }); }); }); diff --git a/packages/graphql/src/schema/resolvers/query/read.ts b/packages/graphql/src/schema/resolvers/query/read.ts index cca3e440dd..b6ce86526d 100644 --- a/packages/graphql/src/schema/resolvers/query/read.ts +++ b/packages/graphql/src/schema/resolvers/query/read.ts @@ -17,30 +17,26 @@ * limitations under the License. */ -import type { GraphQLResolveInfo } from "graphql"; +import { GraphQLInt, GraphQLNonNull, type GraphQLResolveInfo } from "graphql"; import type { SchemaComposer } from "graphql-compose"; -import { QueryOptions } from "../../../graphql/input-objects/QueryOptions"; import type { EntityAdapter } from "../../../schema-model/entity/EntityAdapter"; import { UnionEntityAdapter } from "../../../schema-model/entity/model-adapters/UnionEntityAdapter"; import { translateRead } from "../../../translate"; import { isConcreteEntity } from "../../../translate/queryAST/utils/is-concrete-entity"; -import type { Neo4jFeaturesSettings } from "../../../types"; import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { execute } from "../../../utils"; import getNeo4jResolveTree from "../../../utils/get-neo4j-resolve-tree"; -import { DEPRECATE_OPTIONS_ARGUMENT } from "../../constants"; import { makeSortInput } from "../../generation/sort-and-options-input"; -import { shouldAddDeprecatedFields } from "../../generation/utils"; import type { Neo4jGraphQLComposedContext } from "../composition/wrap-query-and-mutation"; export function findResolver({ entityAdapter, - features, composer, + isLimitRequired, }: { entityAdapter: EntityAdapter; - features?: Neo4jFeaturesSettings; composer: SchemaComposer; + isLimitRequired: boolean | undefined; }) { async function resolve(_root: any, args: any, context: Neo4jGraphQLComposedContext, info: GraphQLResolveInfo) { const resolveTree = getNeo4jResolveTree(info, { args }); @@ -68,21 +64,12 @@ export function findResolver({ } const extraArgs = {}; - if (isConcreteEntity(entityAdapter)) { - if (entityAdapter.annotations.fulltext) { - extraArgs["fulltext"] = { - type: `${entityAdapter.name}Fulltext`, - description: - "Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score.", - }; - } - } const whereArgumentType = entityAdapter.operations.whereInputTypeName; const args = { where: whereArgumentType, - limit: "Int", - offset: "Int", + limit: isLimitRequired ? new GraphQLNonNull(GraphQLInt) : GraphQLInt, + offset: GraphQLInt, ...extraArgs, }; if (!(entityAdapter instanceof UnionEntityAdapter)) { @@ -91,16 +78,6 @@ export function findResolver({ args["sort"] = sortConfig.NonNull.List; } } - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - if (shouldAddDeprecatedFields(features, "deprecatedOptionsArgument")) { - args["options"] = { - type: - entityAdapter instanceof UnionEntityAdapter - ? QueryOptions - : entityAdapter.operations.optionsInputTypeName, - directives: [DEPRECATE_OPTIONS_ARGUMENT], - }; - } return { type: `[${entityAdapter.name}!]!`, diff --git a/packages/graphql/src/schema/resolvers/query/root-connection.ts b/packages/graphql/src/schema/resolvers/query/root-connection.ts index 5313d0ee7e..37220ec974 100644 --- a/packages/graphql/src/schema/resolvers/query/root-connection.ts +++ b/packages/graphql/src/schema/resolvers/query/root-connection.ts @@ -42,10 +42,12 @@ export function rootConnectionResolver({ composer, entityAdapter, propagatedDirectives, + isLimitRequired, }: { composer: SchemaComposer; entityAdapter: InterfaceEntityAdapter | ConcreteEntityAdapter; propagatedDirectives: DirectiveNode[]; + isLimitRequired: boolean | undefined; }) { async function resolve(_root: any, args: any, context: Neo4jGraphQLComposedContext, info: GraphQLResolveInfo) { const resolveTree = getNeo4jResolveTree(info, { args }); @@ -113,19 +115,10 @@ export function rootConnectionResolver({ type: rootConnection.NonNull, resolve, args: { - first: GraphQLInt, + first: isLimitRequired ? new GraphQLNonNull(GraphQLInt) : GraphQLInt, after: GraphQLString, where: entityAdapter.operations.whereInputTypeName, ...(sortArg ? { sort: sortArg.NonNull.List } : {}), - ...(entityAdapter.annotations.fulltext - ? { - fulltext: { - type: entityAdapter.operations.fullTextInputTypeName, - description: - "Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score.", - }, - } - : {}), }, }; } diff --git a/packages/graphql/src/schema/resolvers/subscriptions/where/utils/get-filtering-fn.ts b/packages/graphql/src/schema/resolvers/subscriptions/where/utils/get-filtering-fn.ts index eb8a9f6395..54561aa6b6 100644 --- a/packages/graphql/src/schema/resolvers/subscriptions/where/utils/get-filtering-fn.ts +++ b/packages/graphql/src/schema/resolvers/subscriptions/where/utils/get-filtering-fn.ts @@ -21,8 +21,8 @@ import type { AttributeAdapter } from "../../../../../schema-model/attribute/mod type ComparatorFn = (received: T, filtered: T, fieldMeta?: AttributeAdapter | undefined) => boolean; -const operatorCheckMap = { - NOT: (received: string, filtered: string) => received !== filtered, +const legacyOperatorCheckMap = { + EQ: (received: string, filtered: string) => received == filtered, LT: (received: number | string, filtered: number) => { const parsed = typeof received === "string" ? BigInt(received) : received; @@ -44,23 +44,28 @@ const operatorCheckMap = { return parsed >= filtered; }, STARTS_WITH: (received: string, filtered: string) => received.startsWith(filtered), - NOT_STARTS_WITH: (received: string, filtered: string) => !received.startsWith(filtered), ENDS_WITH: (received: string, filtered: string) => received.endsWith(filtered), - NOT_ENDS_WITH: (received: string, filtered: string) => !received.endsWith(filtered), CONTAINS: (received: string, filtered: string) => received.includes(filtered), - NOT_CONTAINS: (received: string, filtered: string) => !received.includes(filtered), INCLUDES: (received: [string | number], filtered: string | number) => { return received.some((v) => v === filtered); }, - NOT_INCLUDES: (received: [string | number], filtered: string | number) => { - return !received.some((v) => v === filtered); - }, IN: (received: string | number, filtered: [string | number]) => { return filtered.some((v) => v === received); }, - NOT_IN: (received: string | number, filtered: [string | number]) => { - return !filtered.some((v) => v === received); - }, +}; + +const operatorCheckMap = { + ...legacyOperatorCheckMap, + eq: legacyOperatorCheckMap.EQ, + lt: legacyOperatorCheckMap.LT, + lte: legacyOperatorCheckMap.LTE, + gt: legacyOperatorCheckMap.GT, + gte: legacyOperatorCheckMap.GTE, + in: legacyOperatorCheckMap.IN, + startsWith: legacyOperatorCheckMap.STARTS_WITH, + endsWith: legacyOperatorCheckMap.ENDS_WITH, + contains: legacyOperatorCheckMap.CONTAINS, + includes: legacyOperatorCheckMap.INCLUDES, }; export function getFilteringFn( @@ -73,5 +78,9 @@ export function getFilteringFn( const operators = { ...operatorCheckMap, ...overrides }; - return operators[operator]; + const comparatorFunction = operators[operator]; + if (!comparatorFunction) { + throw new Error(`Operator ${operator} not supported`); + } + return comparatorFunction; } diff --git a/packages/graphql/src/schema/resolvers/subscriptions/where/utils/parse-filter-property.ts b/packages/graphql/src/schema/resolvers/subscriptions/where/utils/parse-filter-property.ts index 81b0dacad0..6db512b20a 100644 --- a/packages/graphql/src/schema/resolvers/subscriptions/where/utils/parse-filter-property.ts +++ b/packages/graphql/src/schema/resolvers/subscriptions/where/utils/parse-filter-property.ts @@ -21,31 +21,14 @@ import { parseWhereField } from "../../../../../translate/queryAST/factory/parse export function parseFilterProperty(key: string): { fieldName: string; operator: string | undefined } { // eslint-disable-next-line prefer-const - let { fieldName, operator, isNot } = parseWhereField(key); + let { fieldName, operator } = parseWhereField(key); // These conversions are only temporary necessary until the the _NOT operator exists, after that we can just return the output of parseWhereField if (operator === "EQ") { operator = undefined; } - if (isNot) { - if (operator && isOperatorIsANegateSupportedOperator(operator)) { - operator = `NOT_${operator}`; - } else { - operator = "NOT"; - } - } + return { fieldName, operator }; } -// These are the operator that have a negate version as _NOT_CONTAINS, _NOT_STARTS_WITH etc... . -type NegateSupportedOperator = "CONTAINS" | "STARTS_WITH" | "ENDS_WITH" | "IN" | "INCLUDES"; -/** - * isOperatorIsANegateSupportedOperator returns true if the operator is one of these that have the negate version - * the following is temporary required until the `_NOT` operator is removed. - **/ -function isOperatorIsANegateSupportedOperator(operator: string): operator is NegateSupportedOperator { - return (["CONTAINS", "STARTS_WITH", "ENDS_WITH", "IN", "INCLUDES"] as const).includes( - operator as NegateSupportedOperator - ); -} diff --git a/packages/graphql/src/schema/to-compose.ts b/packages/graphql/src/schema/to-compose.ts index e1643d8a6f..4e98753b22 100644 --- a/packages/graphql/src/schema/to-compose.ts +++ b/packages/graphql/src/schema/to-compose.ts @@ -31,7 +31,8 @@ import { ArgumentAdapter } from "../schema-model/argument/model-adapters/Argumen import type { AttributeAdapter } from "../schema-model/attribute/model-adapters/AttributeAdapter"; import { parseValueNode } from "../schema-model/parser/parse-value-node"; import type { InputField, Neo4jFeaturesSettings } from "../types"; -import { DEPRECATE_IMPLICIT_SET } from "./constants"; +import { DEPRECATE_ARRAY_MUTATIONS, DEPRECATE_MATH_MUTATIONS, DEPRECATE_SET_MUTATION } from "./constants"; +import { getMutationInputFromAttributeType } from "./generation/get-mutation-input-from-attribute-type"; import { shouldAddDeprecatedFields } from "./generation/utils"; import { idResolver } from "./resolvers/field/id"; import { numericalResolver } from "./resolvers/field/numerical"; @@ -136,29 +137,34 @@ export function concreteEntityToUpdateInputFields({ objectFields: AttributeAdapter[]; userDefinedFieldDirectives: Map; additionalFieldsCallbacks: AdditionalFieldsCallback[]; - features?: Neo4jFeaturesSettings; + features: Neo4jFeaturesSettings | undefined; }) { let updateInputFields: InputTypeComposerFieldConfigMapDefinition = {}; for (const field of objectFields) { const newInputField: InputField = { type: field.getInputTypeNames().update.pretty, - directives: [], + directives: [DEPRECATE_SET_MUTATION(field.name)], }; const userDefinedDirectivesOnField = userDefinedFieldDirectives.get(field.name); + let userDefinedDirectives: Directive[] = []; + if (userDefinedDirectivesOnField) { - newInputField.directives = graphqlDirectivesToCompose( + userDefinedDirectives = graphqlDirectivesToCompose( userDefinedDirectivesOnField.filter((directive) => directive.name.value === DEPRECATED) ); - } - if (shouldAddDeprecatedFields(features, "implicitSet")) { - updateInputFields[field.name] = { - type: newInputField.type, - directives: newInputField.directives?.length ? newInputField.directives : [DEPRECATE_IMPLICIT_SET], - }; + + newInputField.directives = userDefinedDirectives; } - updateInputFields[`${field.name}_SET`] = newInputField; + updateInputFields[field.name] = { + type: getMutationInputFromAttributeType(field), + directives: userDefinedDirectives, + }; + + if (shouldAddDeprecatedFields(features, "mutationOperations")) { + updateInputFields[`${field.name}_SET`] = newInputField; + } for (const cb of additionalFieldsCallbacks) { const additionalFields = cb(field, newInputField); @@ -172,9 +178,18 @@ export function concreteEntityToUpdateInputFields({ export function withMathOperators(): AdditionalFieldsCallback { return (attribute: AttributeAdapter, fieldDefinition: InputField): Record => { const fields: Record = {}; + if (attribute.mathModel) { for (const operation of attribute.mathModel.getMathOperations()) { - fields[operation] = fieldDefinition; + const newFieldDefinition = + typeof fieldDefinition === "string" ? { type: fieldDefinition } : { ...fieldDefinition }; + const operationNameUpperCase = operation.split("_")[1]; + if (!operationNameUpperCase) { + throw new Error(`Invalid operation: ${operation}`); + } + const newOperationName = operationNameUpperCase.toLowerCase(); + newFieldDefinition.directives = [DEPRECATE_MATH_MUTATIONS(attribute.name, newOperationName)]; + fields[operation] = newFieldDefinition; } } return fields; @@ -185,14 +200,20 @@ export function withArrayOperators(): AdditionalFieldsCallback { return (attribute: AttributeAdapter): InputTypeComposerFieldConfigMapDefinition => { const fields: InputTypeComposerFieldConfigMapDefinition = {}; if (attribute.listModel) { - fields[attribute.listModel.getPop()] = GraphQLInt; - fields[attribute.listModel.getPush()] = attribute.getInputTypeNames().update.pretty; + fields[attribute.listModel.getPop()] = { + type: GraphQLInt, + directives: [DEPRECATE_ARRAY_MUTATIONS(attribute.name, "pop")], + }; + fields[attribute.listModel.getPush()] = { + type: attribute.getInputTypeNames().update.pretty, + directives: [DEPRECATE_ARRAY_MUTATIONS(attribute.name, "push")], + }; } return fields; }; } -type AdditionalFieldsCallback = ( +export type AdditionalFieldsCallback = ( attribute: AttributeAdapter, fieldDefinition: InputField ) => Record | InputTypeComposerFieldConfigMapDefinition; diff --git a/packages/graphql/src/schema/validation/Neo4jValidationContext.ts b/packages/graphql/src/schema/validation/Neo4jValidationContext.ts new file mode 100644 index 0000000000..89d5d252ff --- /dev/null +++ b/packages/graphql/src/schema/validation/Neo4jValidationContext.ts @@ -0,0 +1,95 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Maybe } from "@graphql-tools/utils"; +import type { + DefinitionNode, + DocumentNode, + EnumTypeDefinitionNode, + GraphQLError, + GraphQLSchema, + InterfaceTypeDefinitionNode, + InterfaceTypeExtensionNode, + ObjectTypeDefinitionNode, + ObjectTypeExtensionNode, + UnionTypeDefinitionNode, + UnionTypeExtensionNode, +} from "graphql"; +import { Kind } from "graphql"; +import { SDLValidationContext } from "graphql/validation/ValidationContext"; + +export type TypeMapWithExtensions = Record< + string, + { + extensions: (ObjectTypeExtensionNode | InterfaceTypeExtensionNode | UnionTypeExtensionNode)[]; + definition: + | ObjectTypeDefinitionNode + | InterfaceTypeDefinitionNode + | UnionTypeDefinitionNode + | EnumTypeDefinitionNode; + } +>; +export class Neo4jValidationContext extends SDLValidationContext { + public readonly typeMapWithExtensions?: TypeMapWithExtensions; + public readonly callbacks?: any; + constructor( + ast: DocumentNode, + schema: Maybe, + onError: (error: GraphQLError) => void, + callbacks?: any + ) { + super(ast, schema, onError); + this.callbacks = callbacks; + this.typeMapWithExtensions = buildTypeMapWithExtensions(ast.definitions); + } +} + +// build a type map to access specific types and their extensions +function buildTypeMapWithExtensions(definitions: Readonly): TypeMapWithExtensions { + return definitions.reduce((acc, def): TypeMapWithExtensions => { + if ( + def.kind === Kind.OBJECT_TYPE_DEFINITION || + def.kind === Kind.INTERFACE_TYPE_DEFINITION || + def.kind === Kind.UNION_TYPE_DEFINITION || + def.kind === Kind.ENUM_TYPE_DEFINITION || + def.kind === Kind.OBJECT_TYPE_EXTENSION || + def.kind === Kind.INTERFACE_TYPE_EXTENSION || + def.kind === Kind.UNION_TYPE_EXTENSION + ) { + const typeName = def.name.value; + if (!acc[typeName]) { + acc[typeName] = { extensions: [], definition: undefined }; + } + if ( + def.kind === Kind.OBJECT_TYPE_EXTENSION || + def.kind === Kind.INTERFACE_TYPE_EXTENSION || + def.kind === Kind.UNION_TYPE_EXTENSION + ) { + if (acc[typeName].extensions) { + acc[typeName].extensions.push(def); + } else { + acc[typeName].extensions = [def]; + } + } else { + acc[typeName].definition = def; + } + } + return acc; + }, {}); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.ts b/packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.ts deleted file mode 100644 index 582eab835a..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - ASTVisitor, - DirectiveNode, - GraphQLArgument, - ArgumentNode, - GraphQLDirective, - GraphQLSchema, -} from "graphql"; -import { coerceInputValue, valueFromASTUntyped, buildASTSchema } from "graphql"; -import type { Maybe } from "graphql/jsutils/Maybe"; -import type { SDLValidationContext } from "graphql/validation/ValidationContext"; -import { VALIDATION_ERROR_CODES } from "../utils/validation-error-codes"; -import type { AssertionResponse } from "./utils/document-validation-error"; -import { createGraphQLError } from "./utils/document-validation-error"; -import { getPathToNode } from "./utils/path-parser"; - -export function DirectiveArgumentOfCorrectType(includeAuthorizationDirectives: boolean = true) { - return function (context: SDLValidationContext): ASTVisitor { - // TODO: find a way to scope this schema instead of creating the whole document - // should only contain dynamic directives and their associated types (typeWhere + jwt payload) - let schema: GraphQLSchema | undefined; - const getSchemaFromDocument = (): GraphQLSchema => { - if (!schema) { - schema = buildASTSchema(context.getDocument(), { assumeValid: true, assumeValidSDL: true }); - } - return schema; - }; - - return { - Directive(directiveNode: DirectiveNode, _key, _parent, path, ancenstors) { - const oneOfAuthorizationDirectives = - includeAuthorizationDirectives && - ["subscriptionsAuthorization", "authorization", "authentication"].reduce( - (genericDirective, oneOfAuthorizationDirectives) => { - if ( - !genericDirective && - directiveNode.name.value - .toLowerCase() - .includes(oneOfAuthorizationDirectives.toLowerCase()) - ) { - genericDirective = oneOfAuthorizationDirectives; - } - return genericDirective; - }, - undefined - ); - const otherDirectives = ["fulltext", "relationship", "node", "customResolver", "cypher"].find( - (applicableDirectiveName) => - directiveNode.name.value.toLowerCase() === applicableDirectiveName.toLowerCase() - ); - - if (!oneOfAuthorizationDirectives && !otherDirectives) { - return; - } - - let directiveName: string; - let directiveDefinition: Maybe; - if (oneOfAuthorizationDirectives) { - directiveDefinition = getSchemaFromDocument().getDirective(directiveNode.name.value); - directiveName = oneOfAuthorizationDirectives; - } else { - directiveDefinition = context.getSchema()?.getDirective(directiveNode.name.value); - directiveName = directiveNode.name.value; - } - - if (!directiveDefinition) { - // Do not report, delegate this report to KnownDirectivesRule - return; - } - const pathToHere = [...getPathToNode(path, ancenstors)[0], `@${directiveName}`]; - for (const argument of directiveNode.arguments || []) { - const argumentDefinition = findArgumentDefinitionNodeByName( - directiveDefinition.args, - argument.name.value - ); - if (!argumentDefinition) { - return; - } - const { isValid, errorMsg, errorPath } = assertArgumentType(argument, argumentDefinition); - if (!isValid) { - context.reportError( - createGraphQLError({ - nodes: [argument, directiveNode], - path: [...pathToHere, argument.name.value, ...errorPath], - errorMsg: `Invalid argument: ${argument.name.value}, error: ${errorMsg}`, - extensions: { - exception: { code: VALIDATION_ERROR_CODES[directiveName.toUpperCase()] }, - }, - }) - ); - } - } - }, - }; - }; -} - -function findArgumentDefinitionNodeByName(args: readonly GraphQLArgument[], name: string): GraphQLArgument | undefined { - return args.find((arg) => arg.name === name); -} - -function assertArgumentType(argumentNode: ArgumentNode, inputValueDefinition: GraphQLArgument): AssertionResponse { - const argType = inputValueDefinition.type; - const argValue = valueFromASTUntyped(argumentNode.value); - - let isValid = true; - let errorMsg = ""; - let errorPath: readonly (string | number)[] = []; - - coerceInputValue(argValue, argType, (path, _invalidValue, error) => { - isValid = false; - errorMsg = error.message; - errorPath = path; - }); - - return { isValid, errorMsg, errorPath }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/authentication.ts b/packages/graphql/src/schema/validation/custom-rules/directives/authentication.ts new file mode 100644 index 0000000000..f3556023df --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/directives/authentication.ts @@ -0,0 +1,106 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { authenticationDirectiveScaffold } from "../../../../graphql/directives/type-dependant-directives/authentication"; +import { isRootType } from "../../../../utils/is-root-type"; +import { asArray } from "../../../../utils/utils"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRootType } from "../utils/location-helpers/is-in-root-type"; +import { fieldIsInSubscriptionType } from "../utils/location-helpers/is-in-subscription-type"; +import { typeIsANodeType } from "../utils/location-helpers/is-node-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateAuthenticationDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + if ( + !fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === authenticationDirectiveScaffold.name + ) + ) { + return; + } + + const isValidLocation = + (fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRootType({ path, ancestors, typeMapWithExtensions })) && + !fieldIsInSubscriptionType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${authenticationDirectiveScaffold.name}" requires in a type with "@node" or in root types: Query, and Mutation`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${authenticationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, path, ancestors) { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + if (!allDirectives.find((directive) => directive.name.value === authenticationDirectiveScaffold.name)) { + return; + } + const isValidLocation = + (typeIsANodeType({ objectTypeDefinitionNode, typeMapWithExtensions }) || + isRootType(objectTypeDefinitionNode)) && + objectTypeDefinitionNode.name.value !== "Subscription"; + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${authenticationDirectiveScaffold.name}" requires in a type with "@node" or in root types: Query, and Mutation`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [objectTypeDefinitionNode], + path: [...pathToNode[0], `@${authenticationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/authorization.ts b/packages/graphql/src/schema/validation/custom-rules/directives/authorization.ts index de88487ecc..958c3c4c2e 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/authorization.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/authorization.ts @@ -16,21 +16,107 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode } from "graphql"; -import { AuthorizationAnnotationArguments } from "../../../../schema-model/annotation/AuthorizationAnnotation"; -import { DocumentValidationError } from "../utils/document-validation-error"; -export function verifyAuthorization() { - return function ({ directiveNode }: { directiveNode: DirectiveNode }) { - for (const arg of AuthorizationAnnotationArguments) { - if (directiveNode.arguments?.find((a) => a.name.value === arg)) { +import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { authenticationDirectiveScaffold } from "../../../../graphql/directives/type-dependant-directives/authentication"; +import { authorizationDirectiveScaffold } from "../../../../graphql/directives/type-dependant-directives/authorization"; +import { asArray } from "../../../../utils/utils"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRootType } from "../utils/location-helpers/is-in-root-type"; +import { typeIsANodeType } from "../utils/location-helpers/is-node-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateAuthorizationDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const authorizationDirective = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === authorizationDirectiveScaffold.name + ); + + if (!authorizationDirective) { return; } - } - throw new DocumentValidationError( - `@authorization requires at least one of ${AuthorizationAnnotationArguments.join(", ")} arguments`, - [] - ); + const isValidLocation = fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + // add specific error message for Root types usage + if (fieldIsInRootType({ path, ancestors, typeMapWithExtensions })) { + throw new DocumentValidationError( + `Directive @${authorizationDirectiveScaffold.name} is not supported on fields of the Query type. Did you mean to use @${authenticationDirectiveScaffold.name}?`, + [] + ); + } + + throw new DocumentValidationError( + `Directive "@${authorizationDirectiveScaffold.name}" requires in a type with "@node"`, + [] + ); + } + if (authorizationDirective.arguments?.length === 0) { + throw new DocumentValidationError( + `@${authorizationDirectiveScaffold.name} requires at least one of ${[...authorizationDirectiveScaffold.args.map((arg) => arg.name)].join(", ")} arguments`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${authorizationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, path, ancestors) { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + const authorizationDirective = allDirectives.find( + (directive) => directive.name.value === authorizationDirectiveScaffold.name + ); + if (!authorizationDirective) { + return; + } + const isValidLocation = typeIsANodeType({ objectTypeDefinitionNode, typeMapWithExtensions }); + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "@${authorizationDirectiveScaffold.name}" requires in a type with "@node"`, + [] + ); + } + if (authorizationDirective.arguments?.length === 0) { + throw new DocumentValidationError( + `@${authorizationDirectiveScaffold.name} requires at least one of ${authorizationDirectiveScaffold.args.join(", ")} arguments`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [objectTypeDefinitionNode], + path: [...pathToNode[0], `@${authorizationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, }; } diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/coalesce.ts b/packages/graphql/src/schema/validation/custom-rules/directives/coalesce.ts index 63319dd22d..22b0be28f8 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/coalesce.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/coalesce.ts @@ -16,51 +16,103 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode, FieldDefinitionNode, EnumTypeDefinitionNode } from "graphql"; + +import type { ASTVisitor, EnumTypeDefinitionNode, FieldDefinitionNode, TypeNode } from "graphql"; import { Kind } from "graphql"; +import { GRAPHQL_BUILTIN_SCALAR_TYPES, SPATIAL_TYPES, TEMPORAL_SCALAR_TYPES } from "../../../../constants"; +import { coalesceDirective } from "../../../../graphql/directives"; +import type { Neo4jValidationContext, TypeMapWithExtensions } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRelationshipPropertiesType } from "../utils/location-helpers/is-in-relationship-properties-type"; +import { getPathToNode } from "../utils/path-parser"; import { assertArgumentHasSameTypeAsField } from "../utils/same-type-argument-as-field"; -import { getInnerTypeName, isArrayType } from "../utils/utils"; -import { GRAPHQL_BUILTIN_SCALAR_TYPES, isSpatial, isTemporal } from "../../../../constants"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; - -export function verifyCoalesce(enums: EnumTypeDefinitionNode[]) { - return function ({ - directiveNode, - traversedDef, - }: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; - }) { - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate - return; - } - const coalesceArg = directiveNode.arguments?.find((a) => a.name.value === "value"); - const expectedType = getInnerTypeName(traversedDef.type); - if (!coalesceArg) { - // delegate to DirectiveArgumentOfCorrectType rule - return; - } +export function validateCoalesceDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + const enumsTypes: EnumTypeDefinitionNode[] = Object.values(typeMapWithExtensions) + .map((type) => type.definition) + .filter((definition): definition is EnumTypeDefinitionNode => definition.kind === Kind.ENUM_TYPE_DEFINITION); - if (!isArrayType(traversedDef)) { - if (isSpatial(expectedType)) { - throw new DocumentValidationError(`@coalesce is not supported by Spatial types.`, ["value"]); + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const coalesce = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === coalesceDirective.name + ); + if (!coalesce) { + return; } - if (isTemporal(expectedType)) { - throw new DocumentValidationError(`@coalesce is not supported by Temporal types.`, ["value"]); + const valueArg = coalesce.arguments?.find((arg) => arg.name.value === "value"); + if (!valueArg) { + return; } - if ( - !GRAPHQL_BUILTIN_SCALAR_TYPES.includes(expectedType) && - !enums.find((x) => x.name.value === expectedType) - ) { - throw new DocumentValidationError( - `@coalesce directive can only be used on types: Int | Float | String | Boolean | ID | Enum`, - [] + const isValidLocation = + fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRelationshipPropertiesType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg, errorPath } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive @"${coalesceDirective.name}" requires in a type with "@node" or within the "@relationshipProperties" directive`, + [] + ); + } + assertTypeIsSupportedByCoalesce(fieldDefinitionNode.type, typeMapWithExtensions); + // for compatibility with previous helper we generate the enumTypes here, but it can be passed the typeMap instead. + assertArgumentHasSameTypeAsField({ + directiveName: coalesceDirective.name, + traversedDef: fieldDefinitionNode, + argument: valueArg, + enums: enumsTypes, + }); + }); + + if (!isValid) { + const pathToNode = getPathToNode(path, ancestors); + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${coalesceDirective.name}`, ...errorPath], + errorMsg, + }) ); } - } - assertArgumentHasSameTypeAsField({ directiveName: "@coalesce", traversedDef, argument: coalesceArg, enums }); + }, }; } + +function assertTypeIsSupportedByCoalesce(typeNode: TypeNode, typeMapWithExtensions: TypeMapWithExtensions): void { + if (typeNode.kind === Kind.LIST_TYPE) { + assertTypeIsSupportedByCoalesce(typeNode.type, typeMapWithExtensions); + } + if (typeNode.kind === Kind.NON_NULL_TYPE) { + assertTypeIsSupportedByCoalesce(typeNode.type, typeMapWithExtensions); + } + + if (typeNode.kind === Kind.NAMED_TYPE) { + if (GRAPHQL_BUILTIN_SCALAR_TYPES.includes(typeNode.name.value)) { + return; + } + + if (SPATIAL_TYPES.includes(typeNode.name.value)) { + throw new DocumentValidationError(`@${coalesceDirective.name} is not supported by Spatial types.`, []); + } + + if (TEMPORAL_SCALAR_TYPES.includes(typeNode.name.value)) { + throw new DocumentValidationError(`@${coalesceDirective.name} is not supported by Temporal types.`, []); + } + // check if the type is an enum + const typeFromMap = typeMapWithExtensions[typeNode.name.value]; + if (typeFromMap?.definition.kind === Kind.ENUM_TYPE_DEFINITION) { + return; + } + + throw new DocumentValidationError( + `@${coalesceDirective.name} directive can only be used on types: Int | Float | String | Boolean | ID | Enum`, + [] + ); + } +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/cypher.ts b/packages/graphql/src/schema/validation/custom-rules/directives/cypher.ts new file mode 100644 index 0000000000..66a7af2841 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/directives/cypher.ts @@ -0,0 +1,68 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, FieldDefinitionNode } from "graphql"; +import { cypherDirective } from "../../../../graphql/directives"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRootType } from "../utils/location-helpers/is-in-root-type"; +import { fieldIsInSubscriptionType } from "../utils/location-helpers/is-in-subscription-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateCypherDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + if ( + !fieldDefinitionNode.directives?.length || + !fieldDefinitionNode.directives.find((directive) => directive.name.value === cypherDirective.name) + ) { + return; + } + const isValidLocation = + (fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRootType({ path, ancestors, typeMapWithExtensions })) && + !fieldIsInSubscriptionType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${cypherDirective.name}" requires in a type with "@node" or on root types: Query, and Mutation`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${cypherDirective.name}`], + errorMsg, + }) + ); + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/default.ts b/packages/graphql/src/schema/validation/custom-rules/directives/default.ts index 68781386c9..7d4b4fa5f9 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/default.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/default.ts @@ -16,77 +16,109 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode, EnumTypeDefinitionNode, FieldDefinitionNode, StringValueNode } from "graphql"; + +import type { ASTVisitor, EnumTypeDefinitionNode, FieldDefinitionNode, TypeNode } from "graphql"; import { Kind } from "graphql"; +import { GraphQLDate } from "graphql-compose"; import { GRAPHQL_BUILTIN_SCALAR_TYPES } from "../../../../constants"; -import { GraphQLDate, GraphQLDateTime, GraphQLLocalDateTime } from "../../../../graphql/scalars"; -import { GraphQLLocalTime, parseLocalTime } from "../../../../graphql/scalars/LocalTime"; -import { GraphQLTime, parseTime } from "../../../../graphql/scalars/Time"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; +import { defaultDirective } from "../../../../graphql/directives"; +import { GraphQLDateTime, GraphQLLocalDateTime, GraphQLLocalTime, GraphQLTime } from "../../../../graphql/scalars"; +import type { Neo4jValidationContext, TypeMapWithExtensions } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRelationshipPropertiesType } from "../utils/location-helpers/is-in-relationship-properties-type"; +import { getPathToNode } from "../utils/path-parser"; import { assertArgumentHasSameTypeAsField } from "../utils/same-type-argument-as-field"; -import { getInnerTypeName, isArrayType } from "../utils/utils"; - -// TODO: schema-generation: save enums as map -export function verifyDefault(enums: EnumTypeDefinitionNode[]) { - return function ({ - directiveNode, - traversedDef, - }: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; - }) { - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate - return; - } +export function validateDefaultDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } - const defaultArg = directiveNode.arguments?.find((a) => a.name.value === "value"); - const expectedType = getInnerTypeName(traversedDef.type); + const enumsTypes: EnumTypeDefinitionNode[] = Object.values(typeMapWithExtensions) + .map((type) => type.definition) + .filter((definition): definition is EnumTypeDefinitionNode => definition.kind === Kind.ENUM_TYPE_DEFINITION); - if (!defaultArg) { - // delegate to DirectiveArgumentOfCorrectType rule - return; - } + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const defDirective = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === defaultDirective.name + ); + if (!defDirective) { + return; + } + const valueArg = defDirective.arguments?.find((arg) => arg.name.value === "value"); + if (!valueArg) { + return; + } + const isValidLocation = + fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRelationshipPropertiesType({ path, ancestors, typeMapWithExtensions }); - if (!isArrayType(traversedDef)) { - if ([GraphQLDateTime.name, GraphQLLocalDateTime.name, GraphQLDate.name].includes(expectedType)) { - if (Number.isNaN(Date.parse((defaultArg?.value as StringValueNode).value))) { - throw new DocumentValidationError( - `@default.${defaultArg.name.value} is not a valid ${expectedType}`, - ["value"] - ); - } - } else if (expectedType === GraphQLTime.name) { - try { - parseTime((defaultArg?.value as StringValueNode).value); - } catch { + const { isValid, errorMsg, errorPath } = assertValid(() => { + if (!isValidLocation) { throw new DocumentValidationError( - `@default.${defaultArg.name.value} is not a valid ${expectedType}`, - ["value"] + `Directive "${defaultDirective.name}" requires in a type with "@node" or within the "@relationshipProperties" directive`, + [] ); } - } else if (expectedType === GraphQLLocalTime.name) { - try { - parseLocalTime((defaultArg?.value as StringValueNode).value); - } catch { - throw new DocumentValidationError( - `@default.${defaultArg.name.value} is not a valid ${expectedType}`, - ["value"] - ); - } - } else if ( - !GRAPHQL_BUILTIN_SCALAR_TYPES.includes(expectedType) && - !enums.some((x) => x.name.value === expectedType) && - expectedType !== "BigInt" - ) { - throw new DocumentValidationError( - `@default directive can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime or LocalTime.`, - [] + assertTypeIsSupportedByDefault(fieldDefinitionNode.type, typeMapWithExtensions); + // for compatibility with previous helper we generate the enumTypes here, but it can be passed the typeMap instead. + assertArgumentHasSameTypeAsField({ + directiveName: defaultDirective.name, + traversedDef: fieldDefinitionNode, + argument: valueArg, + enums: enumsTypes, + }); + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${defaultDirective.name}`, ...errorPath], + errorMsg, + }) ); } - } - assertArgumentHasSameTypeAsField({ directiveName: "@default", traversedDef, argument: defaultArg, enums }); + }, }; } + +function assertTypeIsSupportedByDefault(typeNode: TypeNode, typeMapWithExtensions: TypeMapWithExtensions): void { + if (typeNode.kind === Kind.LIST_TYPE) { + assertTypeIsSupportedByDefault(typeNode.type, typeMapWithExtensions); + } + if (typeNode.kind === Kind.NON_NULL_TYPE) { + assertTypeIsSupportedByDefault(typeNode.type, typeMapWithExtensions); + } + + if (typeNode.kind === Kind.NAMED_TYPE) { + if ( + GRAPHQL_BUILTIN_SCALAR_TYPES.includes(typeNode.name.value) || + [ + GraphQLDateTime.name, + GraphQLLocalDateTime.name, + GraphQLDate.name, + GraphQLTime.name, + GraphQLLocalTime.name, + ].includes(typeNode.name.value) || + typeNode.name.value === BigInt.name + ) { + return; + } + + // check if the type is an enum + const typeFromMap = typeMapWithExtensions[typeNode.name.value]; + if (typeFromMap?.definition.kind === Kind.ENUM_TYPE_DEFINITION) { + return; + } + + throw new DocumentValidationError( + `@${defaultDirective.name} directive can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime or LocalTime.`, + [] + ); + } +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/fulltext.ts b/packages/graphql/src/schema/validation/custom-rules/directives/fulltext.ts index dd008464d3..7ecf72bc4b 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/fulltext.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/fulltext.ts @@ -16,59 +16,114 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode, FieldDefinitionNode } from "graphql"; -import { Kind } from "graphql"; + +import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode, TypeNode } from "graphql"; +import { GraphQLID, GraphQLString, Kind } from "graphql"; +import { fulltextDirective } from "../../../../graphql/directives"; +import type { FulltextField } from "../../../../schema-model/annotation/FulltextAnnotation"; import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; -import type { FulltextContext } from "../../../../types"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; +import { asArray } from "../../../../utils/utils"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { typeIsANodeType } from "../utils/location-helpers/is-node-type"; -export function verifyFulltext({ - directiveNode, - traversedDef, -}: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; -}) { - if (traversedDef.kind !== Kind.OBJECT_TYPE_DEFINITION && traversedDef.kind !== Kind.OBJECT_TYPE_EXTENSION) { - // delegate - return; - } - const indexesArg = directiveNode.arguments?.find((a) => a.name.value === "indexes"); - if (!indexesArg) { - // delegate to DirectiveArgumentOfCorrectType rule - return; +export function validateFulltextDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); } - const indexesValue = parseValueNode(indexesArg.value) as FulltextContext[]; - const compatibleFields = traversedDef.fields?.filter((f) => { - if (f.type.kind === Kind.NON_NULL_TYPE) { - const innerType = f.type.type; - if (innerType.kind === Kind.NAMED_TYPE) { - return ["String", "ID"].includes(innerType.name.value); + return { + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, _path, _ancestors) { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + const appliedFulltextDirective = allDirectives.find( + (directive) => directive.name.value === fulltextDirective.name + ); + if (!appliedFulltextDirective) { + return; + } + const indexesArg = appliedFulltextDirective.arguments?.find((a) => a.name.value === "indexes"); + if (!indexesArg) { + // delegate to DirectiveArgumentOfCorrectType rule + return; } - } - if (f.type.kind === Kind.NAMED_TYPE) { - return ["String", "ID"].includes(f.type.name.value); - } - return false; - }); - indexesValue.forEach((index) => { - const indexName = index.indexName || index.name; - const names = indexesValue.filter((i) => indexName === (i.indexName || i.name)); - if (names.length > 1) { - throw new DocumentValidationError(`@fulltext.indexes invalid value for: ${indexName}. Duplicate name.`, [ - "indexes", - ]); - } - (index.fields || []).forEach((field) => { - const foundField = compatibleFields?.some((f) => f.name.value === field); - if (!foundField) { - throw new DocumentValidationError( - `@fulltext.indexes invalid value for: ${indexName}. Field ${field} is not of type String or ID.`, - ["indexes"] + const compatibleFields = getFulltextCompatibleFields(objectTypeDefinitionNode); + const isValidLocation = typeIsANodeType({ objectTypeDefinitionNode, typeMapWithExtensions }); + const { isValid, errorMsg, errorPath } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${fulltextDirective.name}" requires in a type with "@node"`, + [] + ); + } + const indexesValues = parseValueNode(indexesArg.value) as FulltextField[]; + + indexesValues.forEach((indexValue) => { + const indexName = indexValue.indexName; + const indexNames = indexesValues.filter((i) => indexName === i.indexName); + if (indexNames.length > 1) { + throw new DocumentValidationError( + `@${fulltextDirective.name}.indexes invalid value for: ${indexName}. Duplicate index name.`, + ["indexes"] + ); + } + + const queryName = indexValue.queryName; + const queryNames = indexesValues.filter((i) => queryName === i.queryName); + if (queryNames.length > 1) { + throw new DocumentValidationError( + `@${fulltextDirective.name}.indexes invalid value for: ${queryName}. Duplicate query name.`, + ["indexes"] + ); + } + asArray(indexValue.fields).forEach((indexField) => { + if (!compatibleFields[indexField]) { + throw new DocumentValidationError( + `@${fulltextDirective.name}.indexes invalid value for: ${indexValue.indexName}. Field ${indexField} is not of type String or ID.`, + ["indexes"] + ); + } + }); + }); + }); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [objectTypeDefinitionNode], + path: [objectTypeDefinitionNode.name.value, `@${fulltextDirective.name}`, ...errorPath], + errorMsg, + }) ); } - }); - }); + }, + }; +} + +function getFulltextCompatibleFields( + objectTypeDefinitionNode: ObjectTypeDefinitionNode +): Record { + return (objectTypeDefinitionNode.fields ?? []).reduce( + (acc, f): Record => { + if (isFieldFullTextCompatible(f.type)) { + acc[f.name.value] = f; + } + return acc; + }, + {} as Record + ); +} + +function isFieldFullTextCompatible(fieldType: TypeNode): boolean { + if (fieldType.kind === Kind.NAMED_TYPE) { + return [GraphQLString.name, GraphQLID.name].includes(fieldType.name.value); + } + if (fieldType.kind === Kind.NON_NULL_TYPE) { + return isFieldFullTextCompatible(fieldType.type); + } + return false; } diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/id.ts b/packages/graphql/src/schema/validation/custom-rules/directives/id.ts index cb77b9074d..9a9a990164 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/id.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/id.ts @@ -16,37 +16,62 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode, FieldDefinitionNode } from "graphql"; -import { Kind } from "graphql"; -import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; -import { getInnerTypeName } from "../utils/utils"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; -export function verifyId({ - directiveNode, - traversedDef, -}: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; -}) { - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate - return; - } - // TODO: remove the following as the argument "autogenerate" does not exists anymore - const autogenerateArg = directiveNode.arguments?.find((x) => x.name.value === "autogenerate"); - if (autogenerateArg) { - const autogenerate = parseValueNode(autogenerateArg.value); - if (!autogenerate) { - return; - } +import { GraphQLID, Kind, type ASTVisitor, type FieldDefinitionNode, type TypeNode } from "graphql"; +import { idDirective, relationshipPropertiesDirective } from "../../../../graphql/directives"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRelationshipPropertiesType } from "../utils/location-helpers/is-in-relationship-properties-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateIdDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); } - if (traversedDef.type.kind === Kind.LIST_TYPE) { + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + if (!fieldDefinitionNode.directives?.find((directive) => directive.name.value === idDirective.name)) { + return; + } + const isValidLocation = + fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRelationshipPropertiesType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${idDirective.name}" requires in a type with "@node" or within the "@${relationshipPropertiesDirective.name}" directive`, + [] + ); + } + assertTypeIsSupportedByID(fieldDefinitionNode.type); + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${idDirective.name}`], + errorMsg, + }) + ); + } + }, + }; +} + +function assertTypeIsSupportedByID(type: TypeNode): void { + if (type.kind === Kind.LIST_TYPE) { throw new DocumentValidationError("Cannot autogenerate an array.", ["@id"]); } - if (getInnerTypeName(traversedDef.type) !== "ID") { + if (type.kind === Kind.NON_NULL_TYPE) { + return assertTypeIsSupportedByID(type.type); + } + if (GraphQLID.name !== type.name.value) { throw new DocumentValidationError("Cannot autogenerate a non ID field.", ["@id"]); } } diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/limit.ts b/packages/graphql/src/schema/validation/custom-rules/directives/limit.ts index 888980b418..7300c1d85f 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/limit.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/limit.ts @@ -16,25 +16,108 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode } from "graphql"; -import { DocumentValidationError } from "../utils/document-validation-error"; + +import type { ASTVisitor, DirectiveNode, InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { limitDirective } from "../../../../graphql/directives"; +import { asArray } from "../../../../utils/utils"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import type { AssertionResponse } from "../utils/document-validation-error"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { typeIsANodeType } from "../utils/location-helpers/is-node-type"; import { parseArgumentToInt } from "../utils/utils"; -export function verifyLimit({ directiveNode }: { directiveNode: DirectiveNode }) { - const defaultArg = directiveNode.arguments?.find((a) => a.name.value === "default"); - const maxArg = directiveNode.arguments?.find((a) => a.name.value === "max"); +export function validateLimitDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + return { + InterfaceTypeDefinition( + interfaceTypeDefinitionNode: InterfaceTypeDefinitionNode, + _key, + _parent, + _path, + _ancestors + ) { + const { directives } = interfaceTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[interfaceTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + const appliedLimitDirective = allDirectives.find( + (directive) => directive.name.value === limitDirective.name + ); + if (!appliedLimitDirective) { + return; + } + + const { isValid, errorMsg, errorPath } = assertValid(() => { + assertLimitDirectiveIsValid(appliedLimitDirective); + }); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [interfaceTypeDefinitionNode], + path: [interfaceTypeDefinitionNode.name.value, `@${limitDirective.name}`, ...errorPath], + errorMsg, + }) + ); + } + }, + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, _path, _ancestors) { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + const appliedLimitDirective = allDirectives.find( + (directive) => directive.name.value === limitDirective.name + ); + if (!appliedLimitDirective) { + return; + } + const { isValid, errorMsg, errorPath } = assertValid(() => { + const isValidLocation = typeIsANodeType({ objectTypeDefinitionNode, typeMapWithExtensions }); + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${limitDirective.name}" requires in a type with "@node" or in an interface type`, + [] + ); + } + assertLimitDirectiveIsValid(appliedLimitDirective); + }); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [objectTypeDefinitionNode], + path: [objectTypeDefinitionNode.name.value, `@${limitDirective.name}`, ...errorPath], + errorMsg, + }) + ); + } + }, + }; +} + +// shared assertion code between limit validation between interface and object types +function assertLimitDirectiveIsValid(appliedLimitDirective: DirectiveNode): AssertionResponse | undefined { + const defaultArg = appliedLimitDirective.arguments?.find((a) => a.name.value === "default"); + const maxArg = appliedLimitDirective.arguments?.find((a) => a.name.value === "max"); if (!defaultArg && !maxArg) { // nothing to check, fields are optional return; } const defaultLimit = parseArgumentToInt(defaultArg); const maxLimit = parseArgumentToInt(maxArg); + if (defaultLimit) { const defaultValue = defaultLimit.toNumber(); // default must be greater than 0 if (defaultValue <= 0) { throw new DocumentValidationError( - `@limit.default invalid value: ${defaultValue}. Must be greater than 0.`, + `@${limitDirective.name}.default invalid value: ${defaultValue}. Must be greater than 0.`, ["default"] ); } @@ -43,9 +126,10 @@ export function verifyLimit({ directiveNode }: { directiveNode: DirectiveNode }) const maxValue = maxLimit.toNumber(); // max must be greater than 0 if (maxValue <= 0) { - throw new DocumentValidationError(`@limit.max invalid value: ${maxValue}. Must be greater than 0.`, [ - "max", - ]); + throw new DocumentValidationError( + `@${limitDirective.name}.max invalid value: ${maxValue}. Must be greater than 0.`, + ["max"] + ); } } if (defaultLimit && maxLimit) { @@ -54,7 +138,7 @@ export function verifyLimit({ directiveNode }: { directiveNode: DirectiveNode }) // default must be smaller than max if (maxLimit < defaultLimit) { throw new DocumentValidationError( - `@limit.max invalid value: ${maxValue}. Must be greater than limit.default: ${defaultValue}.`, + `@${limitDirective.name}.max invalid value: ${maxValue}. Must be greater than limit.default: ${defaultValue}.`, ["max"] ); } diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/populated-by.ts b/packages/graphql/src/schema/validation/custom-rules/directives/populated-by.ts new file mode 100644 index 0000000000..a56a7f6f04 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/directives/populated-by.ts @@ -0,0 +1,125 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + GraphQLBoolean, + GraphQLFloat, + GraphQLID, + GraphQLInt, + GraphQLString, + type ASTVisitor, + type FieldDefinitionNode, +} from "graphql"; +import { populatedByDirective } from "../../../../graphql/directives"; +import { + GraphQLBigInt, + GraphQLDate, + GraphQLDateTime, + GraphQLDuration, + GraphQLLocalDateTime, + GraphQLLocalTime, + GraphQLTime, +} from "../../../../graphql/scalars"; +import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRelationshipPropertiesType } from "../utils/location-helpers/is-in-relationship-properties-type"; +import { getPathToNode } from "../utils/path-parser"; +import { getInnerTypeName } from "../utils/utils"; + +export function validatePopulatedByDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + const callbacks = context.callbacks; + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const appliedPopulatedByDirective = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === populatedByDirective.name + ); + if (!appliedPopulatedByDirective) { + return; + } + const callbackArg = appliedPopulatedByDirective.arguments?.find((x) => x.name.value === "callback"); + if (!callbackArg) { + // delegate to DirectiveArgumentOfCorrectType rule + return; + } + const isValidLocation = + fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRelationshipPropertiesType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg, errorPath } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${populatedByDirective.name}" requires in a type with "@node" or within the "@relationshipProperties" directive`, + [] + ); + } + const callbackName = parseValueNode(callbackArg.value); + if (!callbacks) { + throw new DocumentValidationError( + `@${populatedByDirective.name}.callback needs to be provided in features option.`, + ["callback"] + ); + } + if (typeof (callbacks || {})[callbackName] !== "function") { + throw new DocumentValidationError( + `@${populatedByDirective.name}.callback \`${callbackName}\` must be of type Function.`, + ["callback"] + ); + } + if ( + ![ + GraphQLInt.name, + GraphQLFloat.name, + GraphQLString.name, + GraphQLBoolean.name, + GraphQLID.name, + GraphQLBigInt.name, + GraphQLDateTime.name, + GraphQLDate.name, + GraphQLTime.name, + GraphQLLocalDateTime.name, + GraphQLLocalTime.name, + GraphQLDuration.name, + ].includes(getInnerTypeName(fieldDefinitionNode.type)) + ) { + throw new DocumentValidationError( + "@populatedBy can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime, LocalTime or Duration.", + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${populatedByDirective.name}`, ...errorPath], + errorMsg, + }) + ); + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/populatedBy.ts b/packages/graphql/src/schema/validation/custom-rules/directives/populatedBy.ts deleted file mode 100644 index 1c0ce1fce5..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/directives/populatedBy.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Kind, type DirectiveNode, type FieldDefinitionNode } from "graphql"; -import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; -import type { Neo4jGraphQLCallbacks } from "../../../../types"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; -import { getInnerTypeName } from "../utils/utils"; - -export function verifyPopulatedBy(callbacks?: Neo4jGraphQLCallbacks) { - return function ({ - directiveNode, - traversedDef, - }: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; - }) { - const callbackArg = directiveNode.arguments?.find((x) => x.name.value === "callback"); - if (!callbackArg) { - // delegate to DirectiveArgumentOfCorrectType rule - return; - } - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate to KnownDirectivesRule - return; - } - const callbackName = parseValueNode(callbackArg.value); - if (!callbacks) { - throw new DocumentValidationError(`@populatedBy.callback needs to be provided in features option.`, [ - "callback", - ]); - } - if (typeof (callbacks || {})[callbackName] !== "function") { - throw new DocumentValidationError(`@populatedBy.callback \`${callbackName}\` must be of type Function.`, [ - "callback", - ]); - } - if ( - ![ - "Int", - "Float", - "String", - "Boolean", - "ID", - "BigInt", - "DateTime", - "Date", - "Time", - "LocalDateTime", - "LocalTime", - "Duration", - ].includes(getInnerTypeName(traversedDef.type)) - ) { - throw new DocumentValidationError( - "@populatedBy can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime, LocalTime or Duration.", - [] - ); - } - }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/relationship.ts b/packages/graphql/src/schema/validation/custom-rules/directives/relationship.ts index 85bcf24a9f..f6bb52fd20 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/relationship.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/relationship.ts @@ -16,181 +16,136 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { - DirectiveNode, - EnumTypeDefinitionNode, - FieldDefinitionNode, - InterfaceTypeDefinitionNode, - InterfaceTypeExtensionNode, - ObjectTypeDefinitionNode, - UnionTypeDefinitionNode, -} from "graphql"; -import { Kind } from "graphql"; -import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; -import { DocumentValidationError } from "../utils/document-validation-error"; + +import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode, ObjectTypeExtensionNode } from "graphql"; import { - getInheritedTypeNames, - hydrateInterfaceWithImplementedTypesMap, -} from "../utils/interface-to-implementing-types"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; -import { getPrettyName } from "../utils/utils"; + declareRelationshipDirective, + relationshipDirective, + relationshipPropertiesDirective, +} from "../../../../graphql/directives"; +import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInInterfaceType } from "../utils/location-helpers/is-in-interface-type"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { getPathToNode } from "../utils/path-parser"; +import { getInnerTypeName } from "../utils/utils"; -export function verifyRelationshipArgumentValue( - objectTypeToRelationshipsPerRelationshipTypeMap: Map>, - interfaceToImplementationsMap: Map>, - extra?: { - enums: EnumTypeDefinitionNode[]; - interfaces: (InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode)[]; - unions: UnionTypeDefinitionNode[]; - objects: ObjectTypeDefinitionNode[]; +export function validateRelationshipDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); } -) { - return function ({ - directiveNode, - traversedDef, - parentDef, - }: { - directiveNode: DirectiveNode; - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; - parentDef?: ObjectOrInterfaceWithExtensions; - }) { - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate - return; - } - if (!parentDef) { - console.error("No parent definition"); - return; - } - const typeArg = directiveNode.arguments?.find((a) => a.name.value === "type"); - const directionArg = directiveNode.arguments?.find((a) => a.name.value === "direction"); - const propertiesArg = directiveNode.arguments?.find((a) => a.name.value === "properties"); - if (!typeArg && !directionArg) { - // delegate to DirectiveArgumentOfCorrectType rule - return; - } - - if (typeArg && directionArg) { - const fieldType = getPrettyName(traversedDef.type); - const typeValue = parseValueNode(typeArg.value); - const directionValue = parseValueNode(directionArg.value); - const currentRelationship: [string, string, string] = [traversedDef.name.value, directionValue, fieldType]; - verifyRelationshipFields( - parentDef, - currentRelationship, - typeValue, - objectTypeToRelationshipsPerRelationshipTypeMap, - interfaceToImplementationsMap - ); - } - - if (propertiesArg) { - const propertiesValue = parseValueNode(propertiesArg.value); - if (!extra) { - throw new Error("Missing data: Enums, Interfaces, Unions."); - } + return { + // At the object level we need to check that the relationship directive is not applied to multiple fields of the same type + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, _path, _ancestors) { + const fieldTypes = new Map(); + const extensionsFields = ( + (typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions ?? + []) as ObjectTypeExtensionNode[] + ).flatMap((extension) => extension.fields ?? []); - const relationshipPropertiesInterface = extra.interfaces.filter( - (i) => - i.name.value.toLowerCase() === propertiesValue.toLowerCase() && - i.kind === Kind.INTERFACE_TYPE_DEFINITION - ); - - if (relationshipPropertiesInterface.length > 0) { - throw new DocumentValidationError( - `@relationship.properties invalid. The @relationshipProperties directive must be applied to a type and not an interface, a breaking change introduced in version 5.0.0.`, - ["properties"] + [...(objectTypeDefinitionNode.fields ?? []), ...extensionsFields].forEach((field) => { + const appliedRelationship = field.directives?.find( + (directive) => directive.name.value === relationshipDirective.name ); - } + if (!appliedRelationship) { + return; + } + const fieldType = getInnerTypeName(field.type); + const relationshipDirectionArgument = appliedRelationship.arguments?.find( + (a) => a.name.value === "direction" + ); + const relationshipTypeArgument = appliedRelationship.arguments?.find((a) => a.name.value === "type"); + if (!relationshipDirectionArgument || !relationshipTypeArgument) { + // delegate to DirectiveArgumentOfCorrectType rule + return; + } + const directionArgAsString = parseValueNode(relationshipDirectionArgument.value); + const relationshipTypeArgumentAsString = parseValueNode(relationshipTypeArgument.value); + // create a map key of field type + relationship type + relationship direction to check for uniqueness + const validUniqueRelationshipKey = `${fieldType}-${relationshipTypeArgumentAsString}-${directionArgAsString}`; - const relationshipPropertiesType = extra.objects.filter( - (i) => - i.name.value.toLowerCase() === propertiesValue.toLowerCase() && - i.kind === Kind.OBJECT_TYPE_DEFINITION + if (fieldTypes.has(validUniqueRelationshipKey)) { + context.reportError( + createGraphQLError({ + nodes: [field], + path: [ + objectTypeDefinitionNode.name.value, + field.name.value, + `@${relationshipDirective.name}`, + ], + errorMsg: `@${relationshipDirective.name} invalid. Multiple fields of the same type cannot have a relationship with the same direction and type combination.`, + }) + ); + } else { + fieldTypes.set(validUniqueRelationshipKey, field.name.value); + } + }); + }, + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const appliedRelationship = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === relationshipDirective.name ); - - if (relationshipPropertiesType.length > 1) { - throw new DocumentValidationError( - `@relationship.properties invalid. Cannot have more than 1 type represent the relationship properties.`, - ["properties"] - ); + if (!appliedRelationship) { + return; } - if (!relationshipPropertiesType.length) { - throw new DocumentValidationError( - `@relationship.properties invalid. Cannot find type to represent the relationship properties: ${propertiesValue}.`, - ["properties"] - ); + const isValidLocation = fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }); + const [pathToNode, _traversedDef, parentOfTraversedDef] = getPathToNode(path, ancestors); + const typeArg = appliedRelationship.arguments?.find((a) => a.name.value === "type"); + const directionArg = appliedRelationship.arguments?.find((a) => a.name.value === "direction"); + const propertiesArg = appliedRelationship.arguments?.find((a) => a.name.value === "properties"); + if (!typeArg || !directionArg) { + // delegate to DirectiveArgumentOfCorrectType rule + return; } - const isRelationshipPropertiesTypeAnnotated = relationshipPropertiesType[0]?.directives?.some( - (d) => d.name.value === "relationshipProperties" - ); - if (!isRelationshipPropertiesTypeAnnotated) { - throw new DocumentValidationError( - `@relationship.properties invalid. Properties type ${propertiesValue} must use directive \`@relationshipProperties\`.`, - ["properties"] + const { isValid, errorMsg, errorPath } = assertValid(() => { + if (!isValidLocation) { + if (fieldIsInInterfaceType({ path, ancestors, typeMapWithExtensions })) { + // throw more specific error for interface types as in the past it was possible to have relationships on interfaces + throw new DocumentValidationError( + `Invalid directive usage: Directive @${relationshipDirective.name} is not supported on fields of interface types (${parentOfTraversedDef?.name.value}). Since version 5.0.0, interface fields can only have @${declareRelationshipDirective.name}. Please add the @relationship directive to the fields in all types which implement it.`, + [] + ); + } + throw new DocumentValidationError( + `Directive "${relationshipDirective.name}" requires in a type with "@node"`, + [] + ); + } + if (propertiesArg) { + // find the relationshipProperties type, if type does not exist, throw error + const propertiesArgAsString = parseValueNode(propertiesArg.value); + const propertiesType = typeMapWithExtensions[propertiesArgAsString]?.definition; + if (!propertiesType) { + throw new DocumentValidationError( + `@${relationshipDirective.name}.properties invalid. Cannot find type to represent the relationship properties: ${propertiesArgAsString}.`, + ["properties"] + ); + } + if ( + !propertiesType.directives?.find( + (directive) => directive.name.value === relationshipPropertiesDirective.name + ) + ) { + throw new DocumentValidationError( + `@${relationshipDirective.name}.properties invalid. Properties type ${propertiesArgAsString} must use directive \`@${relationshipPropertiesDirective.name}\`.`, + ["properties"] + ); + } + } + }); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode, `@${relationshipDirective.name}`, ...errorPath], + errorMsg, + }) ); } - } + }, }; } - -function getUpdatedRelationshipFieldsForCurrentType( - relationshipFieldsForCurrentType: Map | undefined, - currentRelationship: [string, string, string], - typeValue: any -) { - const updatedRelationshipFieldsForCurrentType = - relationshipFieldsForCurrentType || new Map(); - const updatedRelationshipsWithSameRelationshipType = ( - relationshipFieldsForCurrentType?.get(typeValue) || [] - ).concat([currentRelationship]); - updatedRelationshipFieldsForCurrentType.set(typeValue, updatedRelationshipsWithSameRelationshipType); - return updatedRelationshipFieldsForCurrentType; -} - -function checkRelationshipFieldsForDuplicates( - relationshipFieldsForDependentType: Map | undefined, - currentRelationship: [string, string, string], - typeValue: any -) { - if (!relationshipFieldsForDependentType) { - return; - } - const relationshipsWithSameRelationshipType = relationshipFieldsForDependentType.get(typeValue); - relationshipsWithSameRelationshipType?.forEach(([fieldName, existingDirection, existingFieldType]) => { - if ( - fieldName !== currentRelationship[0] && - existingDirection === currentRelationship[1] && - existingFieldType === currentRelationship[2] - ) { - throw new DocumentValidationError( - `@relationship invalid. Multiple fields of the same type cannot have a relationship with the same direction and type combination.`, - [] - ); - } - }); -} - -function verifyRelationshipFields( - parentDef: ObjectOrInterfaceWithExtensions, - currentRelationship: [string, string, string], - typeValue: any, - objectTypeToRelationshipsPerRelationshipTypeMap: Map>, - interfaceToImplementationsMap: Map> -) { - const relationshipFieldsForCurrentType = objectTypeToRelationshipsPerRelationshipTypeMap.get(parentDef.name.value); - checkRelationshipFieldsForDuplicates(relationshipFieldsForCurrentType, currentRelationship, typeValue); - objectTypeToRelationshipsPerRelationshipTypeMap.set( - parentDef.name.value, - getUpdatedRelationshipFieldsForCurrentType(relationshipFieldsForCurrentType, currentRelationship, typeValue) - ); - - const inheritedTypeNames = getInheritedTypeNames(parentDef, interfaceToImplementationsMap); - inheritedTypeNames.forEach((typeName) => { - const inheritedRelationshipFields = objectTypeToRelationshipsPerRelationshipTypeMap.get(typeName); - checkRelationshipFieldsForDuplicates(inheritedRelationshipFields, currentRelationship, typeValue); - }); - - hydrateInterfaceWithImplementedTypesMap(parentDef, interfaceToImplementationsMap); -} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/relay-id.ts b/packages/graphql/src/schema/validation/custom-rules/directives/relay-id.ts new file mode 100644 index 0000000000..c76836612a --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/directives/relay-id.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, FieldDefinitionNode } from "graphql"; +import { relayIdDirective } from "../../../../graphql/directives"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateRelayIdDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + if (!fieldDefinitionNode.directives?.find((directive) => directive.name.value === relayIdDirective.name)) { + return; + } + const isValidLocation = fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${relayIdDirective.name}" requires in a type with "@node"`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${relayIdDirective.name}`], + errorMsg, + }) + ); + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/subscriptionAuthorization.ts b/packages/graphql/src/schema/validation/custom-rules/directives/subscriptionAuthorization.ts new file mode 100644 index 0000000000..e5d733021d --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/directives/subscriptionAuthorization.ts @@ -0,0 +1,100 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { subscriptionsAuthorizationDirectiveScaffold } from "../../../../graphql/directives/type-dependant-directives/subscriptions-authorization"; +import { asArray } from "../../../../utils/utils"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { typeIsANodeType } from "../utils/location-helpers/is-node-type"; +import { getPathToNode } from "../utils/path-parser"; + +export function validateSubscriptionAuthorizationDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); + } + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + const appliedSubscriptionDirective = fieldDefinitionNode.directives?.find( + (directive) => directive.name.value === subscriptionsAuthorizationDirectiveScaffold.name + ); + + if (!appliedSubscriptionDirective) { + return; + } + + const isValidLocation = fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${subscriptionsAuthorizationDirectiveScaffold.name}" requires in a type with "@node"`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${subscriptionsAuthorizationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent, path, ancestors) { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + const appliedSubscriptionDirective = allDirectives.find( + (directive) => directive.name.value === subscriptionsAuthorizationDirectiveScaffold.name + ); + if (!appliedSubscriptionDirective) { + return; + } + const isValidLocation = typeIsANodeType({ objectTypeDefinitionNode, typeMapWithExtensions }); + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${subscriptionsAuthorizationDirectiveScaffold.name}" requires in a type with "@node"`, + [] + ); + } + }); + const pathToNode = getPathToNode(path, ancestors); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [objectTypeDefinitionNode], + path: [...pathToNode[0], `@${subscriptionsAuthorizationDirectiveScaffold.name}`], + errorMsg, + }) + ); + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/timestamp.ts b/packages/graphql/src/schema/validation/custom-rules/directives/timestamp.ts index 47a8bbb342..f519c80e73 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/timestamp.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/timestamp.ts @@ -16,27 +16,68 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { FieldDefinitionNode } from "graphql"; + +import type { ASTVisitor, FieldDefinitionNode, TypeNode } from "graphql"; import { Kind } from "graphql"; -import { getInnerTypeName } from "../utils/utils"; -import { DocumentValidationError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; +import { timestampDirective } from "../../../../graphql/directives"; +import { GraphQLDateTime, GraphQLTime } from "../../../../graphql/scalars"; +import type { Neo4jValidationContext } from "../../Neo4jValidationContext"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { fieldIsInNodeType } from "../utils/location-helpers/is-in-node-type"; +import { fieldIsInRelationshipPropertiesType } from "../utils/location-helpers/is-in-relationship-properties-type"; +import { getPathToNode } from "../utils/path-parser"; -export function verifyTimestamp({ - traversedDef, -}: { - traversedDef: ObjectOrInterfaceWithExtensions | FieldDefinitionNode; -}) { - if (traversedDef.kind !== Kind.FIELD_DEFINITION) { - // delegate - return; +export function validateTimestampDirective(context: Neo4jValidationContext): ASTVisitor { + const typeMapWithExtensions = context.typeMapWithExtensions; + if (!typeMapWithExtensions) { + throw new Error("No typeMapWithExtensions found in the context"); } - if (traversedDef.type.kind === Kind.LIST_TYPE) { - throw new DocumentValidationError("Cannot autogenerate an array.", ["@timestamp"]); + return { + FieldDefinition(fieldDefinitionNode: FieldDefinitionNode, _key, _parent, path, ancestors) { + if ( + !fieldDefinitionNode.directives?.find((directive) => directive.name.value === timestampDirective.name) + ) { + return; + } + const isValidLocation = + fieldIsInNodeType({ path, ancestors, typeMapWithExtensions }) || + fieldIsInRelationshipPropertiesType({ path, ancestors, typeMapWithExtensions }); + + const { isValid, errorMsg } = assertValid(() => { + if (!isValidLocation) { + throw new DocumentValidationError( + `Directive "${timestampDirective.name}" requires in a type with "@node" or within the "@relationshipProperties" directive`, + [] + ); + } + assertTypeIsSupportedByTimestamp(fieldDefinitionNode.type); + }); + const pathToNode = getPathToNode(path, ancestors); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [...pathToNode[0], `@${timestampDirective.name}`], + errorMsg, + }) + ); + } + }, + }; +} + +function assertTypeIsSupportedByTimestamp(type: TypeNode): void { + if (type.kind === Kind.NON_NULL_TYPE) { + return assertTypeIsSupportedByTimestamp(type.type); + } + if (type.kind === Kind.LIST_TYPE) { + throw new DocumentValidationError("Cannot autogenerate an array.", [`@${timestampDirective.name}`]); } - if (!["DateTime", "Time"].includes(getInnerTypeName(traversedDef.type))) { + + if (![GraphQLDateTime.name, GraphQLTime.name].includes(type.name.value)) { throw new DocumentValidationError("Cannot timestamp Temporal fields lacking time zone information.", [ - "@timestamp", + `@${timestampDirective.name}`, ]); } } diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive-field-location.ts b/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive-field-location.ts deleted file mode 100644 index 095790ae1a..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive-field-location.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - ASTVisitor, - DirectiveNode, - FieldDefinitionNode, - InterfaceTypeDefinitionNode, - InterfaceTypeExtensionNode, - ObjectTypeDefinitionNode, - ObjectTypeExtensionNode, -} from "graphql"; -import { Kind } from "graphql"; -import type { SDLValidationContext } from "graphql/validation/ValidationContext"; -import * as directives from "../../../../graphql/directives"; -import { typeDependantDirectivesScaffolds } from "../../../../graphql/directives/type-dependant-directives/scaffolds"; -import { SCHEMA_CONFIGURATION_FIELD_DIRECTIVES } from "../../../../schema-model/library-directives"; -import { isInArray } from "../../../../utils/is-in-array"; -import { DocumentValidationError, assertValid, createGraphQLError } from "../utils/document-validation-error"; -import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; -import { getPathToNode } from "../utils/path-parser"; - -export function ValidDirectiveAtFieldLocation(context: SDLValidationContext): ASTVisitor { - return { - Directive(directiveNode: DirectiveNode, _key, _parent, path, ancestors) { - const [pathToNode, traversedDef, parentOfTraversedDef] = getPathToNode(path, ancestors); - if (!traversedDef || traversedDef.kind !== Kind.FIELD_DEFINITION) { - // this rule only checks field location - return; - } - if (!parentOfTraversedDef) { - console.error("No parent of last definition traversed"); - return; - } - const shouldRunThisRule = isDirectiveValidAtLocation({ - directiveNode, - traversedDef, - parentDef: parentOfTraversedDef, - }); - - if (!shouldRunThisRule) { - return; - } - - const { isValid, errorMsg, errorPath } = assertValid(shouldRunThisRule); - if (!isValid) { - context.reportError( - createGraphQLError({ - nodes: [traversedDef], - path: [...pathToNode, ...errorPath], - errorMsg, - }) - ); - } - }, - }; -} - -function isDirectiveValidAtLocation({ - directiveNode, - traversedDef, - parentDef, -}: { - directiveNode: DirectiveNode; - traversedDef: FieldDefinitionNode; - parentDef: ObjectOrInterfaceWithExtensions; -}) { - if (isLocationFieldOfRootType(parentDef)) { - return () => - validFieldOfRootTypeLocation({ - directiveNode, - traversedDef: traversedDef, - parentDef, - }); - } - if (isLocationFieldOfInterfaceType(parentDef)) { - return () => - validFieldOfInterfaceTypeLocation({ - directiveNode, - parentDef, - }); - } - - return; -} - -function isLocationFieldOfRootType( - parentDef: ObjectOrInterfaceWithExtensions -): parentDef is ObjectTypeDefinitionNode | ObjectTypeExtensionNode { - return ( - parentDef && - (parentDef.kind === Kind.OBJECT_TYPE_DEFINITION || parentDef.kind === Kind.OBJECT_TYPE_EXTENSION) && - ["Query", "Mutation", "Subscription"].includes(parentDef.name.value) - ); -} - -function isLocationFieldOfInterfaceType( - parentDef: ObjectOrInterfaceWithExtensions -): parentDef is InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode { - return ( - parentDef && - (parentDef.kind === Kind.INTERFACE_TYPE_DEFINITION || parentDef.kind === Kind.INTERFACE_TYPE_EXTENSION) - ); -} - -function noDirectivesAllowedAtLocation({ - directiveNode, - parentDef, -}: { - directiveNode: DirectiveNode; - parentDef: ObjectOrInterfaceWithExtensions; -}) { - const allDirectivesDefinedByNeo4jGraphQL = Object.values(directives).concat(typeDependantDirectivesScaffolds); - const directiveAtInvalidLocation = allDirectivesDefinedByNeo4jGraphQL.find( - (d) => d.name === directiveNode.name.value - ); - if (directiveAtInvalidLocation) { - if (directiveAtInvalidLocation.name === "relationship" && parentDef.kind === Kind.INTERFACE_TYPE_DEFINITION) { - throw new DocumentValidationError( - `Invalid directive usage: Directive @${directiveAtInvalidLocation.name} is not supported on fields of interface types (${parentDef.name.value}). Since version 5.0.0, interface fields can only have @declareRelationship. Please add the @relationship directive to the fields in all types which implement it.`, - [`@${directiveNode.name.value}`] - ); - } else { - throw new DocumentValidationError( - `Invalid directive usage: Directive @${directiveAtInvalidLocation.name} is not supported on fields of the ${parentDef.name.value} type.`, - [`@${directiveNode.name.value}`] - ); - } - } -} - -/** only the @cypher and @authentication directives are valid on fields of Root types: Query, Mutation; no directives valid on fields of Subscription */ -function validFieldOfRootTypeLocation({ - directiveNode, - traversedDef, - parentDef, -}: { - directiveNode: DirectiveNode; - traversedDef: FieldDefinitionNode; - parentDef: ObjectTypeDefinitionNode | ObjectTypeExtensionNode; -}) { - if (parentDef.name.value !== "Subscription") { - // some directives are valid on Query | Mutation - if (directiveNode.name.value === "cypher") { - // @cypher is valid - return; - } - if (directiveNode.name.value === "authentication") { - // @authentication is valid - return; - } - const isDirectiveCombinedWithCypher = traversedDef.directives?.some( - (directive) => directive.name.value === "cypher" - ); - // explicitly checked for "enhanced" error messages - if (directiveNode.name.value === "authorization" && isDirectiveCombinedWithCypher) { - throw new DocumentValidationError( - `Invalid directive usage: Directive @authorization is not supported on fields of the ${parentDef.name.value} type. Did you mean to use @authentication?`, - [`@${directiveNode.name.value}`] - ); - } - } - noDirectivesAllowedAtLocation({ directiveNode, parentDef }); -} - -/** only a subset of field directives are allowed on interface fields */ -function validFieldOfInterfaceTypeLocation({ - directiveNode, - parentDef, -}: { - directiveNode: DirectiveNode; - parentDef: InterfaceTypeDefinitionNode | InterfaceTypeExtensionNode; -}) { - if (isInArray(SCHEMA_CONFIGURATION_FIELD_DIRECTIVES, directiveNode.name.value)) { - return; - } - if (directiveNode.name.value === "declareRelationship") { - // allow @declareRelationship as an instruction for schema generation - return; - } - if (directiveNode.name.value === "private") { - // allow @private for now - return; - } - - noDirectivesAllowedAtLocation({ directiveNode, parentDef }); -} diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive.ts b/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive.ts deleted file mode 100644 index a217490723..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/directives/valid-directive.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - ASTVisitor, - DirectiveNode, - EnumTypeDefinitionNode, - InterfaceTypeDefinitionNode, - ObjectTypeDefinitionNode, - UnionTypeDefinitionNode, -} from "graphql"; -import type { SDLValidationContext } from "graphql/validation/ValidationContext"; -import type { Neo4jGraphQLCallbacks } from "../../../../types"; -import type { ValidationFunction } from "../utils/document-validation-error"; -import { assertValid, createGraphQLError } from "../utils/document-validation-error"; -import { getPathToNode } from "../utils/path-parser"; -import { verifyAuthorization } from "./authorization"; -import { verifyCoalesce } from "./coalesce"; -import { verifyDefault } from "./default"; -import { verifyFulltext } from "./fulltext"; -import { verifyLimit } from "./limit"; -import { verifyPopulatedBy } from "./populatedBy"; -import { verifyRelationshipArgumentValue } from "./relationship"; - -function getValidationFunction( - directiveName: string, - objectTypeToFieldNameDirectionAndFieldTypePerRelationshipTypeMap: Map< - string, - Map - >, - interfaceToImplementationsMap: Map>, - extra: { - enums: EnumTypeDefinitionNode[]; - interfaces: InterfaceTypeDefinitionNode[]; - unions: UnionTypeDefinitionNode[]; - objects: ObjectTypeDefinitionNode[]; - }, - callbacks?: Neo4jGraphQLCallbacks -): ValidationFunction | undefined { - switch (directiveName) { - case "coalesce": - return verifyCoalesce(extra.enums); - case "default": - return verifyDefault(extra.enums); - case "fulltext": - return verifyFulltext; - case "populatedBy": - return verifyPopulatedBy(callbacks); - case "limit": - return verifyLimit; - case "relationship": - return verifyRelationshipArgumentValue( - objectTypeToFieldNameDirectionAndFieldTypePerRelationshipTypeMap, - interfaceToImplementationsMap, - extra - ); - case "authorization": - return verifyAuthorization(); - default: - return; - } -} - -function extraDefinitionsProvided(extra: { - enums?: EnumTypeDefinitionNode[]; - interfaces?: InterfaceTypeDefinitionNode[]; - unions?: UnionTypeDefinitionNode[]; - objects?: ObjectTypeDefinitionNode[]; -}): extra is { - enums: EnumTypeDefinitionNode[]; - interfaces: InterfaceTypeDefinitionNode[]; - unions: UnionTypeDefinitionNode[]; - objects: ObjectTypeDefinitionNode[]; -} { - if (!extra.enums || !extra.interfaces || !extra.unions || !extra.objects) { - return false; - } - return true; -} - -export function directiveIsValid( - extra: { - enums?: EnumTypeDefinitionNode[]; - interfaces?: InterfaceTypeDefinitionNode[]; - unions?: UnionTypeDefinitionNode[]; - objects?: ObjectTypeDefinitionNode[]; - }, - callbacks?: Neo4jGraphQLCallbacks -) { - if (!extraDefinitionsProvided(extra)) { - throw new Error("Missing data."); - } - return function (context: SDLValidationContext): ASTVisitor { - const objectTypeToFieldNameDirectionAndFieldTypePerRelationshipTypeMap = new Map< - string, - Map - >(); - const interfaceToImplementationsMap = new Map>(); - return { - Directive(directiveNode: DirectiveNode, _key, _parent, path, ancestors) { - const validationFn = getValidationFunction( - directiveNode.name.value, - objectTypeToFieldNameDirectionAndFieldTypePerRelationshipTypeMap, - interfaceToImplementationsMap, - extra, - callbacks - ); - if (!validationFn) { - return; - } - - const [pathToNode, traversedDef, parentOfTraversedDef] = getPathToNode(path, ancestors); - const pathToHere = [...pathToNode, `@${directiveNode.name.value}`]; - - if (!traversedDef) { - console.error("No last definition traversed"); - return; - } - - const { isValid, errorMsg, errorPath } = assertValid(() => - validationFn({ - directiveNode, - traversedDef, - parentDef: parentOfTraversedDef, - }) - ); - if (!isValid) { - context.reportError( - createGraphQLError({ - nodes: [directiveNode, traversedDef], - path: [...pathToHere, ...errorPath], - errorMsg, - }) - ); - } - }, - }; - }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/single-relationship.ts b/packages/graphql/src/schema/validation/custom-rules/error-single-relationships.ts similarity index 58% rename from packages/graphql/src/schema/validation/custom-rules/warnings/single-relationship.ts rename to packages/graphql/src/schema/validation/custom-rules/error-single-relationships.ts index d5f00f2c6d..467f02b852 100644 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/single-relationship.ts +++ b/packages/graphql/src/schema/validation/custom-rules/error-single-relationships.ts @@ -19,29 +19,27 @@ import type { ASTVisitor, FieldDefinitionNode, ListTypeNode, NonNullTypeNode } from "graphql"; import { Kind } from "graphql"; -import { relationshipDirective } from "../../../../graphql/directives"; - -export function WarnIfSingleRelationships(): ASTVisitor { - let warningAlreadyIssued = false; +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import { relationshipDirective } from "../../../graphql/directives"; +import { createGraphQLError } from "./utils/document-validation-error"; +export function ErrorIfSingleRelationships(context: SDLValidationContext): ASTVisitor { return { FieldDefinition(field: FieldDefinitionNode) { - if (!warningAlreadyIssued) { - let isRelationship = false; - for (const directive of field.directives ?? []) { - if (directive.name.value === relationshipDirective.name) { - isRelationship = true; - } + let isRelationship = false; + for (const directive of field.directives ?? []) { + if (directive.name.value === relationshipDirective.name) { + isRelationship = true; } + } - const isList = Boolean(getListTypeNode(field)); - - if (isRelationship && !isList) { - console.warn( - "Using @relationship directive on a non-list element is deprecated and will be removed in next major version." - ); - warningAlreadyIssued = true; - } + const isList = Boolean(getListTypeNode(field)); + if (isRelationship && !isList) { + context.reportError( + createGraphQLError({ + errorMsg: `Using @relationship directive on a non-list property "${field.name.value}" is not supported.`, + }) + ); } }, }; diff --git a/packages/graphql/src/schema/validation/custom-rules/features/valid-relationship-properties.ts b/packages/graphql/src/schema/validation/custom-rules/features/valid-relationship-properties.ts index c07f65e384..5f6af9ce9f 100644 --- a/packages/graphql/src/schema/validation/custom-rules/features/valid-relationship-properties.ts +++ b/packages/graphql/src/schema/validation/custom-rules/features/valid-relationship-properties.ts @@ -91,22 +91,5 @@ function assertRelationshipProperties(traversedDef: ObjectOrInterfaceWithExtensi throw new DocumentValidationError(`Invalid @relationshipProperties field: ${message}`, errorPath); } }); - - if (field.directives) { - const forbiddenDirectives = [ - "authorization", - "authentication", - "subscriptionsAuthorization", - "relationship", - "cypher", - ]; - const foundForbiddenDirective = field.directives.find((d) => forbiddenDirectives.includes(d.name.value)); - if (foundForbiddenDirective) { - throw new DocumentValidationError( - `Invalid @relationshipProperties field: Cannot use the @${foundForbiddenDirective.name.value} directive on relationship properties.`, - errorPath - ); - } - } }); } diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/get-parent-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/get-parent-type.ts new file mode 100644 index 0000000000..85e6d4dc74 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/get-parent-type.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type ASTNode } from "graphql"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getPathToNode } from "../path-parser"; + +export function getParentType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}) { + const [pathToNode, _traversedDef, parentOfTraversedDef] = getPathToNode(path, ancestors); + if (!parentOfTraversedDef) { + throw new Error( + `Internal validation error: field with path: ${pathToNode.join(", ")} is in a type that does not exist in the typeMapWithExtensions` + ); + } + const parentTypeAndExtensions = typeMapWithExtensions[parentOfTraversedDef.name.value]; + if (!parentTypeAndExtensions) { + throw new Error( + `Internal validation error: field with path: ${pathToNode.join(", ")} is in a type that does not exist in the typeMapWithExtensions` + ); + } + return parentTypeAndExtensions; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-interface-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-interface-type.ts new file mode 100644 index 0000000000..e2cfbdc18d --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-interface-type.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Kind, type ASTNode } from "graphql"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getParentType } from "./get-parent-type"; + +export function fieldIsInInterfaceType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const parentTypeAndExtensions = getParentType({ path, ancestors, typeMapWithExtensions }); + return parentTypeAndExtensions.definition.kind === Kind.INTERFACE_TYPE_DEFINITION; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-node-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-node-type.ts new file mode 100644 index 0000000000..cb6e1c95ed --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-node-type.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type ASTNode } from "graphql"; +import { nodeDirective } from "../../../../../graphql/directives"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getParentType } from "./get-parent-type"; + +export function fieldIsInNodeType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const parentTypeAndExtensions = getParentType({ path, ancestors, typeMapWithExtensions }); + const allDirectives = [ + ...(parentTypeAndExtensions.definition.directives ?? []), + ...parentTypeAndExtensions.extensions.flatMap((ext) => ext.directives ?? []), + ]; + return !!allDirectives?.find((directive) => directive.name.value === nodeDirective.name); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-relationship-properties-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-relationship-properties-type.ts new file mode 100644 index 0000000000..95badf3e07 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-relationship-properties-type.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type ASTNode } from "graphql"; +import { relationshipPropertiesDirective } from "../../../../../graphql/directives"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getParentType } from "./get-parent-type"; + +export function fieldIsInRelationshipPropertiesType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const parentTypeAndExtensions = getParentType({ path, ancestors, typeMapWithExtensions }); + const allDirectives = [ + ...(parentTypeAndExtensions.definition.directives ?? []), + ...parentTypeAndExtensions.extensions.flatMap((ext) => ext.directives ?? []), + ]; + return !!allDirectives?.find((directive) => directive.name.value === relationshipPropertiesDirective.name); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-root-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-root-type.ts new file mode 100644 index 0000000000..e23893872d --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-root-type.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Kind, type ASTNode } from "graphql"; +import { isRootType } from "../../../../../utils/is-root-type"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getParentType } from "./get-parent-type"; + +export function fieldIsInRootType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const parentTypeAndExtensions = getParentType({ path, ancestors, typeMapWithExtensions }); + return ( + parentTypeAndExtensions.definition.kind === Kind.OBJECT_TYPE_DEFINITION && + isRootType(parentTypeAndExtensions.definition) + ); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-subscription-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-subscription-type.ts new file mode 100644 index 0000000000..168802d3f3 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-in-subscription-type.ts @@ -0,0 +1,35 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type ASTNode } from "graphql"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; +import { getParentType } from "./get-parent-type"; + +export function fieldIsInSubscriptionType({ + path, + ancestors, + typeMapWithExtensions, +}: { + path: readonly (string | number)[]; + ancestors: readonly (ASTNode | readonly ASTNode[])[]; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const parentTypeAndExtensions = getParentType({ path, ancestors, typeMapWithExtensions }); + return parentTypeAndExtensions.definition.name.value === "Subscription"; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-node-type.ts b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-node-type.ts new file mode 100644 index 0000000000..c81d7d43bd --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/utils/location-helpers/is-node-type.ts @@ -0,0 +1,41 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { type ObjectTypeDefinitionNode } from "graphql"; +import { nodeDirective } from "../../../../../graphql/directives"; +import { asArray } from "../../../../../utils/utils"; +import type { TypeMapWithExtensions } from "../../../Neo4jValidationContext"; + +export function typeIsANodeType({ + objectTypeDefinitionNode, + typeMapWithExtensions, +}: { + objectTypeDefinitionNode: ObjectTypeDefinitionNode; + typeMapWithExtensions: TypeMapWithExtensions; +}): boolean { + const { directives } = objectTypeDefinitionNode; + const objectTypeExtensionNodes = typeMapWithExtensions[objectTypeDefinitionNode.name.value]?.extensions; + + const extensionsDirectives = asArray(objectTypeExtensionNodes).flatMap((extensionNode) => { + return extensionNode.directives ?? []; + }); + const allDirectives = [...(directives ?? []), ...extensionsDirectives]; + + return !!allDirectives?.find((directive) => directive.name.value === nodeDirective.name); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/same-type-argument-as-field.ts b/packages/graphql/src/schema/validation/custom-rules/utils/same-type-argument-as-field.ts index 8cdf5e82e9..6baa59887a 100644 --- a/packages/graphql/src/schema/validation/custom-rules/utils/same-type-argument-as-field.ts +++ b/packages/graphql/src/schema/validation/custom-rules/utils/same-type-argument-as-field.ts @@ -17,8 +17,18 @@ * limitations under the License. */ import type { ArgumentNode, EnumTypeDefinitionNode, FieldDefinitionNode, ValueNode } from "graphql"; -import { Kind } from "graphql"; +import { GraphQLFloat, GraphQLID, GraphQLInt, GraphQLString, Kind } from "graphql"; import { isSpatial, isTemporal } from "../../../../constants"; +import { + GraphQLBigInt, + GraphQLDate, + GraphQLDateTime, + GraphQLLocalDateTime, + GraphQLLocalTime, + GraphQLTime, +} from "../../../../graphql/scalars"; +import { parseLocalTime } from "../../../../graphql/scalars/LocalTime"; +import { validateTime } from "../../../../graphql/scalars/Time"; import { DocumentValidationError } from "./document-validation-error"; import { fromValueKind, getInnerTypeName, isArrayType } from "./utils"; @@ -38,7 +48,7 @@ export function assertArgumentHasSameTypeAsField({ if (isArrayType(traversedDef)) { if (argument.value.kind !== Kind.LIST) { throw new DocumentValidationError( - `${directiveName}.${argument.name.value} on ${expectedType} list fields must be a list of ${expectedType} values`, + `@${directiveName}.${argument.name.value} on ${expectedType} list fields must be a list of ${expectedType} values`, [argument.name.value] ); } @@ -50,7 +60,7 @@ export function assertArgumentHasSameTypeAsField({ } if (!doTypesMatch(expectedType, v, enums)) { throw new DocumentValidationError( - `${directiveName}.${argument.name.value} on ${expectedType} list fields must be a list of ${expectedType} values`, + `@${directiveName}.${argument.name.value} on ${expectedType} list fields must be a list of ${expectedType} values`, [argument.name.value] ); } @@ -58,7 +68,7 @@ export function assertArgumentHasSameTypeAsField({ } else { if (!doTypesMatch(expectedType, argument.value, enums)) { throw new DocumentValidationError( - `${directiveName}.${argument.name.value} on ${expectedType} fields must be of type ${expectedType}`, + `@${directiveName}.${argument.name.value} on ${expectedType} fields must be of type ${expectedType}`, [argument.name.value] ); } @@ -66,21 +76,71 @@ export function assertArgumentHasSameTypeAsField({ } function doTypesMatch(expectedType: string, argumentValueType: ValueNode, enums: EnumTypeDefinitionNode[]): boolean { - const isSpatialOrTemporal = isSpatial(expectedType) || isTemporal(expectedType); - if (isSpatialOrTemporal) { - return true; + if (expectedType === GraphQLID.name) { + return Boolean(fromValueKind(argumentValueType, enums, expectedType) === GraphQLString.name.toLowerCase()); } - if (expectedType.toLowerCase() === "id") { - return !!(fromValueKind(argumentValueType, enums, expectedType)?.toLowerCase() === "string"); + + if (expectedType === GraphQLBigInt.name) { + const kind = fromValueKind(argumentValueType, enums, expectedType); + return Boolean(kind == GraphQLInt.name.toLowerCase() || kind == GraphQLString.name.toLowerCase()); } - if (expectedType.toLowerCase() === "bigint") { + + if (expectedType === GraphQLFloat.name) { const kind = fromValueKind(argumentValueType, enums, expectedType)?.toLowerCase(); - return !!(kind == "int" || kind == "string"); + return Boolean(kind == GraphQLInt.name.toLowerCase() || kind == GraphQLFloat.name.toLowerCase()); } - if (expectedType.toLowerCase() === "float") { - const kind = fromValueKind(argumentValueType, enums, expectedType)?.toLowerCase(); - return !!(kind == "int" || kind == "float"); + if ([GraphQLDateTime.name, GraphQLLocalDateTime.name, GraphQLDate.name].includes(expectedType)) { + return isValidDateTime(argumentValueType); + } + + if (expectedType === GraphQLTime.name) { + return isValidTime(argumentValueType); } + + if (expectedType === GraphQLLocalTime.name) { + return isValidLocalTime(argumentValueType); + } + + // TODO: Spatial types and some of the temporal types values are not yet validated. + if (isSpatial(expectedType) || isTemporal(expectedType)) { + return true; + } + return fromValueKind(argumentValueType, enums, expectedType)?.toLowerCase() === expectedType.toLowerCase(); } + +// TODO: isValidTime and isValidLocalTime are as they were in the original `@default` validation rules, +// this is an improvement compared to the previous implementation as initially it was tested only for the default directive, +// but it can be improved further without using the try-catch, + +function isValidTime(valueNode: ValueNode): boolean { + if (valueNode.kind !== Kind.STRING) { + return false; + } + try { + validateTime(valueNode.value); + } catch { + return false; + } + return true; +} + +function isValidLocalTime(valueNode: ValueNode): boolean { + if (valueNode.kind !== Kind.STRING) { + return false; + } + try { + parseLocalTime(valueNode.value); + } catch { + return false; + } + return true; +} + +function isValidDateTime(valueNode: ValueNode): boolean { + if (valueNode.kind !== Kind.STRING) { + return false; + } + return !Number.isNaN(Date.parse(valueNode.value)); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/utils/utils.ts b/packages/graphql/src/schema/validation/custom-rules/utils/utils.ts index 15ee9b7638..39c7699020 100644 --- a/packages/graphql/src/schema/validation/custom-rules/utils/utils.ts +++ b/packages/graphql/src/schema/validation/custom-rules/utils/utils.ts @@ -16,10 +16,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { ArgumentNode, EnumTypeDefinitionNode, FieldDefinitionNode, TypeNode, ValueNode } from "graphql"; -import { Kind } from "graphql"; +import type { + ArgumentNode, + EnumTypeDefinitionNode, + FieldDefinitionNode, + GraphQLArgument, + TypeNode, + ValueNode, +} from "graphql"; +import { coerceInputValue, Kind, valueFromASTUntyped } from "graphql"; import * as neo4j from "neo4j-driver"; import { parseValueNode } from "../../../../schema-model/parser/parse-value-node"; +import type { AssertionResponse } from "./document-validation-error"; export function getInnerTypeName(typeNode: TypeNode): string { if (typeNode.kind === Kind.LIST_TYPE) { @@ -86,3 +94,30 @@ export function isArrayType(traversedDef: FieldDefinitionNode) { (traversedDef.type.kind === Kind.NON_NULL_TYPE && traversedDef.type.type.kind === Kind.LIST_TYPE) ); } + +export function findArgumentDefinitionNodeByName( + args: readonly GraphQLArgument[], + name: string +): GraphQLArgument | undefined { + return args.find((arg) => arg.name === name); +} + +export function assertArgumentType( + argumentNode: ArgumentNode, + inputValueDefinition: GraphQLArgument +): AssertionResponse { + const argType = inputValueDefinition.type; + const argValue = valueFromASTUntyped(argumentNode.value); + + let isValid = true; + let errorMsg = ""; + let errorPath: ReadonlyArray = []; + + coerceInputValue(argValue, argType, (path, _invalidValue, error) => { + isValid = false; + errorMsg = error.message; + errorPath = path; + }); + + return { isValid, errorMsg, errorPath }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-field-types.ts b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-field-types.ts index d58e114e5e..0316ce87a0 100644 --- a/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-field-types.ts +++ b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-field-types.ts @@ -21,8 +21,6 @@ import type { ASTVisitor, DirectiveNode, FieldDefinitionNode } from "graphql"; import { Kind } from "graphql"; import type { SDLValidationContext } from "graphql/validation/ValidationContext"; import { SCALAR_TYPES } from "../../../../constants"; -import { verifyId } from "../directives/id"; -import { verifyTimestamp } from "../directives/timestamp"; import type { ValidationFunction } from "../utils/document-validation-error"; import { DocumentValidationError, assertValid, createGraphQLError } from "../utils/document-validation-error"; import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; @@ -63,10 +61,6 @@ function verifyRelationshipFieldType({ function getValidationFunction(directiveName: string): ValidationFunction | undefined { switch (directiveName) { - case "id": - return verifyId; - case "timestamp": - return verifyTimestamp; case "relationship": return verifyRelationshipFieldType; case "declareRelationship": diff --git a/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts new file mode 100644 index 0000000000..a7a658497d --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/valid-types/valid-list-in-node-type.ts @@ -0,0 +1,108 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Kind, type ASTVisitor, type ObjectTypeDefinitionNode, type TypeNode } from "graphql"; + +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import { + cypherDirective, + nodeDirective, + relationshipDirective, + relationshipPropertiesDirective, +} from "../../../../graphql/directives"; +import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; +import { getInnerTypeName, getPrettyName } from "../utils/utils"; + +/** + * Validates that list types used in type annotated with the node directive are supported by Neo4j + **/ +export function ValidListInNodeType(context: SDLValidationContext): ASTVisitor { + return { + ObjectTypeDefinition(objectTypeDefinitionNode: ObjectTypeDefinitionNode, _key, _parent) { + const { directives } = objectTypeDefinitionNode; + + const nodeUsage = directives?.find((directive) => directive.name.value === nodeDirective.name); + const relationshipPropertiesUsage = directives?.find( + (directive) => directive.name.value === relationshipPropertiesDirective.name + ); + if (!directives) { + return; // Skip when no directives are present + } + + if (!nodeUsage && !relationshipPropertiesUsage) { + return; // Skip if is the type is neither annotated with node nor relationshipProperties + } + + objectTypeDefinitionNode.fields?.forEach((fieldDefinitionNode) => { + const { type, directives } = fieldDefinitionNode; + if ( + directives && + directives.some( + (directive) => + directive.name.value === cypherDirective.name || + directive.name.value === relationshipDirective.name + ) + ) { + return; // Skip cypher fields and relationship fields, relationship fields have their own validation + } + const { isValid, errorMsg, errorPath } = assertValid(() => { + const typePath = getTypePath(type); + if (typePath.includes(Kind.LIST_TYPE)) { + const wrappedType = getInnerTypeName(type); + const validTypePaths: string[][] = [ + [Kind.LIST_TYPE, Kind.NON_NULL_TYPE, wrappedType], + [Kind.NON_NULL_TYPE, Kind.LIST_TYPE, Kind.NON_NULL_TYPE, wrappedType], + ]; + if (!findTypePathInTypePaths(typePath, validTypePaths)) { + const typeStr = getPrettyName(type); + + const directiveName = (nodeUsage ?? relationshipPropertiesUsage)?.name?.value; + throw new DocumentValidationError( + `List of nullable elements are not supported in "@${directiveName}" types. Found: ${typeStr}`, + [] + ); + } + } + }); + + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [fieldDefinitionNode], + path: [objectTypeDefinitionNode.name.value, fieldDefinitionNode.name.value, ...errorPath], + errorMsg, + }) + ); + } + }); + }, + }; +} + +function getTypePath(typeNode: TypeNode, currentPath: string[] = []): string[] { + if (typeNode.kind === Kind.NON_NULL_TYPE || typeNode.kind === Kind.LIST_TYPE) { + return getTypePath(typeNode.type, [...currentPath, typeNode.kind]); + } + return [...currentPath, typeNode.name.value]; +} + +function findTypePathInTypePaths(typePathToFind: string[], typePaths: string[][]): boolean { + const typePathString = typePathToFind.join(); + return typePaths.some((typePath) => typePathString === typePath.join()); +} diff --git a/packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.test.ts b/packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.test.ts similarity index 85% rename from packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.test.ts rename to packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.test.ts index 276ed95160..f48db95d2a 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directive-argument-of-correct-type.test.ts +++ b/packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.test.ts @@ -17,12 +17,12 @@ * limitations under the License. */ -import type { GraphQLError } from "graphql"; +import { GraphQLSchema, type GraphQLError } from "graphql"; import { gql } from "graphql-tag"; import { validateSDL } from "../validate-sdl"; -import { DirectiveArgumentOfCorrectType } from "./directive-argument-of-correct-type"; +import { ValidateAuthorizationLikeDirectives } from "./validate-authorization-like-directives"; -describe("DirectiveArgumentOfCorrectType", () => { +describe("validate-authorization-like-directives", () => { describe("for Scalar", () => { describe("for Int", () => { test("should returns no errors for valid Int", () => { @@ -34,7 +34,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -48,7 +48,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -65,7 +65,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -84,7 +84,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -98,7 +98,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -115,7 +115,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -134,7 +134,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -148,7 +148,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -165,7 +165,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -184,7 +184,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -198,7 +198,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -215,7 +215,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -240,7 +240,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -259,7 +259,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -283,7 +283,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -302,7 +302,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -321,7 +321,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -338,7 +338,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -366,7 +366,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -389,7 +389,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(0); }); @@ -412,7 +412,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -438,7 +438,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -464,7 +464,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( @@ -490,7 +490,7 @@ describe("DirectiveArgumentOfCorrectType", () => { name: String! } `; - const errors = validateSDL(userDocument, [DirectiveArgumentOfCorrectType()]); + const errors = validateSDL(userDocument, [ValidateAuthorizationLikeDirectives], new GraphQLSchema({})); expect(errors).toBeInstanceOf(Array); expect(errors).toHaveLength(1); expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( diff --git a/packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.ts b/packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.ts new file mode 100644 index 0000000000..4d31bc4427 --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/validate-authorization-like-directives.ts @@ -0,0 +1,83 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, DirectiveNode } from "graphql"; +import { extendSchema } from "graphql"; +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import { VALIDATION_ERROR_CODES } from "../utils/validation-error-codes"; +import { createGraphQLError } from "./utils/document-validation-error"; +import { getPathToNode } from "./utils/path-parser"; +import { assertArgumentType, findArgumentDefinitionNodeByName } from "./utils/utils"; + +/** + * ValidateAuthorizationLikeDirectives validates the directives subscriptionsAuthorization, authorization, authentication + **/ +export function ValidateAuthorizationLikeDirectives(context: SDLValidationContext): ASTVisitor { + const validationSchema = context.getSchema(); + if (!validationSchema) { + throw new Error("Validation error: schema is not available"); + } + const schema = extendSchema(validationSchema, context.getDocument(), { assumeValid: true, assumeValidSDL: true }); + + return { + Directive(directiveNode: DirectiveNode, _key, _parent, path, ancestors) { + const authorizationLikeDirective = ["subscriptionsAuthorization", "authorization", "authentication"].find( + (authLikeDirective) => { + // find authorizationLike directive generated for validation purposes such a MovieAuthorization + // see packages/graphql/src/graphql/directives/type-dependant-directives/authorization.ts as example + return directiveNode.name.value.toLowerCase().includes(authLikeDirective.toLowerCase()); + } + ); + if (!authorizationLikeDirective) { + return; + } + + const directiveDefinition = schema.getDirective(directiveNode.name.value); + + if (!directiveDefinition) { + // Do not report, delegate this report to KnownDirectivesRule + return; + } + + const pathToHere = [...getPathToNode(path, ancestors)[0], `@${authorizationLikeDirective}`]; + for (const argument of directiveNode.arguments ?? []) { + const argumentDefinition = findArgumentDefinitionNodeByName( + directiveDefinition.args, + argument.name.value + ); + if (!argumentDefinition) { + return; // If argument name is not found, delegate to KnownArgumentNamesRule + } + const { isValid, errorMsg, errorPath } = assertArgumentType(argument, argumentDefinition); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [argument, directiveNode], + path: [...pathToHere, argument.name.value, ...errorPath], + errorMsg: `Invalid argument: ${argument.name.value}, error: ${errorMsg}`, + extensions: { + exception: { code: VALIDATION_ERROR_CODES[authorizationLikeDirective.toUpperCase()] }, + }, + }) + ); + } + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.test.ts b/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.test.ts new file mode 100644 index 0000000000..10bcc033af --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLError } from "graphql"; +import { GraphQLSchema } from "graphql"; +import { gql } from "graphql-tag"; +import { cypherDirective, fulltextDirective, nodeDirective } from "../../../graphql/directives"; +import { validateSDL } from "../validate-sdl"; +import { ValidateNeo4jDirectiveArgumentsValue } from "./validate-neo4j-directive-arguments-value"; + +describe("validateNeo4jDirectiveArgumentsValue", () => { + describe("node", () => { + test("should returns no errors for valid node arguments", () => { + const userDocument = gql` + type User @node(labels: ["Person"]) { + id: ID! + name: String! + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(0); + }); + + test("should returns errors for invalid node arguments", () => { + const userDocument = gql` + type User @node(labels: 1) { + id: ID! + name: String! + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(1); + expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( + `"Invalid argument: labels, error: String cannot represent a non string value: 1"` + ); + }); + }); + + describe("cypher", () => { + test("should returns no errors for valid cypher arguments", () => { + const userDocument = gql` + type User @node { + id: ID! + name: String! + @cypher( + statement: """ + MATCH (n:Movie) RETURN n as movie + """ + columnName: "movie" + ) + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective, cypherDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(0); + }); + + test("should returns errors for invalid cypher arguments", () => { + const userDocument = gql` + type User @node { + id: ID! + name: String! + @cypher( + statement: """ + MATCH (n:Movie) RETURN n as movie + """ + columnName: 3 + ) + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective, cypherDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(1); + expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( + `"Invalid argument: columnName, error: String cannot represent a non string value: 3"` + ); + }); + }); + + describe("fulltext", () => { + test("should returns no errors for valid fulltext arguments", () => { + const userDocument = gql` + type User @fulltext(indexes: [{ fields: ["id", "name"], indexName: "UserIndex" }]) { + id: ID! + name: String! + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective, cypherDirective, fulltextDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(0); + }); + + test("should returns errors for invalid fulltext arguments", () => { + const userDocument = gql` + type User @fulltext(indexes: [{ fields: ["id", "name"], indexName: ["UserIndex"] }]) { + id: ID! + name: String! + } + `; + const schemaToExtend = new GraphQLSchema({ + directives: [nodeDirective, cypherDirective, fulltextDirective], + }); + const errors = validateSDL(userDocument, [ValidateNeo4jDirectiveArgumentsValue], schemaToExtend); + expect(errors).toBeInstanceOf(Array); + expect(errors).toHaveLength(1); + expect((errors[0] as GraphQLError).message).toMatchInlineSnapshot( + `"Invalid argument: indexes, error: String cannot represent a non string value: [\\"UserIndex\\"]"` + ); + }); + }); +}); diff --git a/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.ts b/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.ts new file mode 100644 index 0000000000..d7c5b4bd7f --- /dev/null +++ b/packages/graphql/src/schema/validation/custom-rules/validate-neo4j-directive-arguments-value.ts @@ -0,0 +1,89 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ASTVisitor, DirectiveNode } from "graphql"; +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import { + customResolverDirective, + cypherDirective, + fulltextDirective, + nodeDirective, + relationshipDirective, +} from "../../../graphql/directives"; +import { VALIDATION_ERROR_CODES } from "../utils/validation-error-codes"; +import { createGraphQLError } from "./utils/document-validation-error"; +import { getPathToNode } from "./utils/path-parser"; +import { assertArgumentType, findArgumentDefinitionNodeByName } from "./utils/utils"; + +export function ValidateNeo4jDirectiveArgumentsValue(context: SDLValidationContext): ASTVisitor { + const schema = context.getSchema(); + if (!schema) { + throw new Error("Validation error: schema is not available"); + } + + return { + Directive(directiveNode: DirectiveNode, _key, _parent, path, ancestors) { + const neo4jDirectiveToValidate = [ + fulltextDirective.name, + relationshipDirective.name, + nodeDirective.name, + customResolverDirective.name, + cypherDirective.name, + ].find( + (applicableDirectiveName) => + directiveNode.name.value.toLowerCase() === applicableDirectiveName.toLowerCase() + ); + + if (!neo4jDirectiveToValidate) { + return; + } + + const directiveDefinition = schema.getDirective(directiveNode.name.value); + const directiveName = directiveNode.name.value; + + if (!directiveDefinition) { + // Do not report, delegate this report to KnownDirectivesRule + return; + } + const pathToHere = [...getPathToNode(path, ancestors)[0], `@${directiveName}`]; + for (const argument of directiveNode.arguments ?? []) { + const argumentDefinition = findArgumentDefinitionNodeByName( + directiveDefinition.args, + argument.name.value + ); + if (!argumentDefinition) { + return; // If argument name is not found, delegate to KnownArgumentNamesRule + } + const { isValid, errorMsg, errorPath } = assertArgumentType(argument, argumentDefinition); + if (!isValid) { + context.reportError( + createGraphQLError({ + nodes: [argument, directiveNode], + path: [...pathToHere, argument.name.value, ...errorPath], + errorMsg: `Invalid argument: ${argument.name.value}, error: ${errorMsg}`, + extensions: { + exception: { code: VALIDATION_ERROR_CODES[directiveName.toUpperCase()] }, + }, + }) + ); + } + } + }, + }; +} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-private.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-private.ts deleted file mode 100644 index ecd4500360..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-private.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; -import { privateDirective } from "../../../../graphql/directives"; - -export function WarnPrivateDeprecation() { - return function (): ASTVisitor { - let warningAlreadyIssued = false; - - return { - ObjectTypeDefinition(objectTypeDefinition: ObjectTypeDefinitionNode) { - if (["Query", "Mutation", "Subscription"].includes(objectTypeDefinition.name.value)) { - return; - } - if (warningAlreadyIssued) { - return; - } - let hasPrivateDirective = false; - for (const field of objectTypeDefinition.fields || []) { - for (const directive of field.directives ?? []) { - if (directive.name.value === privateDirective.name) { - hasPrivateDirective = true; - } - } - } - - if (hasPrivateDirective) { - console.warn(`Future library versions will not support @private directive.`); - warningAlreadyIssued = true; - } - }, - }; - }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts deleted file mode 100644 index 39e0c14315..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/deprecated-unique.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; -import { uniqueDirective } from "../../../../graphql/directives"; - -export function WarnUniqueDeprecation() { - return function (): ASTVisitor { - let warningAlreadyIssued = false; - - return { - ObjectTypeDefinition(objectTypeDefinition: ObjectTypeDefinitionNode) { - if (["Query", "Mutation", "Subscription"].includes(objectTypeDefinition.name.value)) { - return; - } - if (warningAlreadyIssued) { - return; - } - let hasUniqueDirective = false; - for (const field of objectTypeDefinition.fields || []) { - for (const directive of field.directives ?? []) { - if (directive.name.value === uniqueDirective.name) { - hasUniqueDirective = true; - } - } - } - - if (hasUniqueDirective) { - console.warn(`Future library versions will not support @unique directive.`); - warningAlreadyIssued = true; - } - }, - }; - }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts deleted file mode 100644 index f4302f5024..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/list-of-lists.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Kind } from "graphql"; -import type { ListTypeNode, NonNullTypeNode, ASTVisitor, FieldDefinitionNode } from "graphql"; - -export function WarnIfListOfListsFieldDefinition(): ASTVisitor { - let warningAlreadyIssued = false; - - return { - FieldDefinition(field: FieldDefinitionNode) { - if (!warningAlreadyIssued) { - const listTypeNode = getListTypeNode(field); - - if (listTypeNode) { - if (getListTypeNode(listTypeNode)) { - console.warn( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." - ); - warningAlreadyIssued = true; - } - } - } - }, - }; -} - -function getListTypeNode(definition: FieldDefinitionNode | ListTypeNode | NonNullTypeNode): ListTypeNode | undefined { - if (definition.type.kind === Kind.NON_NULL_TYPE) { - return getListTypeNode(definition.type); - } - - if (definition.type.kind === Kind.LIST_TYPE) { - return definition.type; - } - - return; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/query-direction-deprecated-values.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/query-direction-deprecated-values.ts deleted file mode 100644 index b0718b1b32..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/query-direction-deprecated-values.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Kind, type ASTVisitor, type DirectiveNode } from "graphql"; -import { RelationshipQueryDirectionOption } from "../../../../constants"; -import { relationshipDirective } from "../../../../graphql/directives"; - -export function WarnIfQueryDirectionIsUsedWithDeprecatedValues(): ASTVisitor { - let warningAlreadyIssued = false; - - return { - Directive(directive: DirectiveNode) { - if (warningAlreadyIssued) { - return; - } - if (relationshipDirective.name === directive.name.value) { - const queryDirection = directive.arguments?.find((arg) => arg.name.value === "queryDirection"); - const queryDirectionValue = - queryDirection && queryDirection.value.kind === Kind.ENUM && queryDirection.value.value; - if ( - queryDirectionValue && - [ - RelationshipQueryDirectionOption[RelationshipQueryDirectionOption.DEFAULT_DIRECTED], - RelationshipQueryDirectionOption[RelationshipQueryDirectionOption.DEFAULT_UNDIRECTED], - ].includes(RelationshipQueryDirectionOption[queryDirectionValue]) - ) { - console.warn( - `Found @relationship argument "queryDirection" used with ${queryDirectionValue} which is deprecated. \n These default values were used to set a default for the "directed" argument, which is also now deprecated.` - ); - warningAlreadyIssued = true; - } - - if ( - queryDirectionValue && - [ - RelationshipQueryDirectionOption[RelationshipQueryDirectionOption.DIRECTED_ONLY], - RelationshipQueryDirectionOption[RelationshipQueryDirectionOption.UNDIRECTED_ONLY], - ].includes(RelationshipQueryDirectionOption[queryDirectionValue]) - ) { - console.warn( - `Found @relationship argument "queryDirection" used with ${queryDirectionValue} which is deprecated. Please use "DIRECTED" or "UNDIRECTED" instead.` - ); - warningAlreadyIssued = true; - } - } - }, - }; -} diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/subscriptions-authorization-missing.test.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/subscriptions-authorization-missing.test.ts index 10ce0adec5..fdfc4a3aa7 100644 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/subscriptions-authorization-missing.test.ts +++ b/packages/graphql/src/schema/validation/custom-rules/warnings/subscriptions-authorization-missing.test.ts @@ -81,6 +81,7 @@ describe("WarnObjectFieldsWithoutResolver", () => { test("does not warn if both directives are used on type", () => { const doc = gql` type User + @node @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { id: ID! diff --git a/packages/graphql/src/schema/validation/custom-rules/warnings/warn-if-type-is-not-marked-as-node.ts b/packages/graphql/src/schema/validation/custom-rules/warnings/warn-if-type-is-not-marked-as-node.ts deleted file mode 100644 index 53b8665fac..0000000000 --- a/packages/graphql/src/schema/validation/custom-rules/warnings/warn-if-type-is-not-marked-as-node.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ASTVisitor, ObjectTypeDefinitionNode } from "graphql"; -import { jwt, nodeDirective, relationshipPropertiesDirective } from "../../../../graphql/directives"; - -export function WarnIfTypeIsNotMarkedAsNode() { - return function (): ASTVisitor { - let warningAlreadyIssued = false; - - return { - ObjectTypeDefinition(objectTypeDefinition: ObjectTypeDefinitionNode) { - if (["Query", "Mutation", "Subscription"].includes(objectTypeDefinition.name.value)) { - return; - } - if (warningAlreadyIssued) { - return; - } - let hasNodeDirective = false; - for (const directive of objectTypeDefinition.directives ?? []) { - if ([relationshipPropertiesDirective.name, jwt.name].includes(directive.name.value)) { - return; - } - if (directive.name.value === nodeDirective.name) { - hasNodeDirective = true; - } - } - if (!hasNodeDirective) { - console.warn( - `Future library versions will require marking all types representing Neo4j nodes with the @node directive.` - ); - warningAlreadyIssued = true; - } - }, - }; - }; -} diff --git a/packages/graphql/src/schema/validation/node-is-missing.test.ts b/packages/graphql/src/schema/validation/node-is-missing.test.ts new file mode 100644 index 0000000000..5dfe33de77 --- /dev/null +++ b/packages/graphql/src/schema/validation/node-is-missing.test.ts @@ -0,0 +1,389 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { + EnumTypeDefinitionNode, + InterfaceTypeDefinitionNode, + ObjectTypeDefinitionNode, + UnionTypeDefinitionNode, +} from "graphql"; +import gql from "graphql-tag"; +import validateDocument from "./validate-document"; + +const additionalDefinitions = { + enums: [] as EnumTypeDefinitionNode[], + interfaces: [] as InterfaceTypeDefinitionNode[], + unions: [] as UnionTypeDefinitionNode[], + objects: [] as ObjectTypeDefinitionNode[], +}; +describe("directive is required to be in a type with @node", () => { + test("should raise when @relationship is not used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "relationship" requires in a type with "@node"'); + }); + + test("should not raise when @relationship is used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie @node { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow('Directive "relationship" requires in a type with "@node"'); + }); + + test("should raise when @cypher is not used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie { + title: String + actors: [Actor!]! @cypher(statement: "MATCH (a:Actor) RETURN a", columnName: "a") + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "cypher" requires in a type with "@node"'); + }); + + test("should not raise when @cypher is used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie @node { + title: String + actors: [Actor!]! @cypher(statement: "MATCH (a:Actor) RETURN a", columnName: "a") + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow('Directive "cypher" requires in a type with "@node"'); + }); + + test("should raise when @populatedBy is not used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie { + title: String + actors: [Actor!]! @populatedBy(callback: "myCallback") + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: { + populatedBy: { + callbacks: { myCallback: () => "hello" }, + }, + }, + }); + expect(executeValidate).toThrow('Directive "populatedBy" requires in a type with "@node"'); + }); + + test("should not raise when @populatedBy is used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie @node { + title: String + actors: [Actor!]! @populatedBy(callback: "myCallback") + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: { + populatedBy: { + callbacks: { myCallback: () => "hello" }, + }, + }, + }); + expect(executeValidate).not.toThrow('Directive "populatedBy" requires in a type with "@node"'); + }); + + test("should raise when @relayId is not used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie { + title: String + id: ID! @relayId + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "relayId" requires in a type with "@node"'); + }); + + test("should not raise when @relayId is used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie @node { + title: String + id: ID! @relayId + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow('Directive "relayId" requires in a type with "@node"'); + }); + + test("should raise when @authorization is not used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie { + id: ID! + title: String + actors: [Actor!]! @authorization(validate: [{ where: { id: "1" } }]) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "@authorization" requires in a type with "@node"'); + }); + + test("should not raise when @authorization is used within the @node directive", () => { + const doc = gql` + type Actor @node { + name: String + } + type Movie @node { + id: ID! + title: String + actors: [Actor!]! @authorization(validate: [{ where: { id: "1" } }]) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow(); + }); + + test("should raise when @authorization is not used within the @node directive at object level", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie @authorization(validate: [{ where: { id: "1" } }]) { + id: ID! + title: String + actors: [Actor!]! + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "@authorization" requires in a type with "@node"'); + }); + + test("should not raise when @authorization is used within the @node directive at object level", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie @node @authorization(validate: [{ where: { id: "1" } }]) { + id: ID! + title: String + actors: [Actor!]! + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow(); + }); + + test("should raise when @authorization is not used within the @node directive in type extension", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie { + id: ID! + title: String + } + + extend type Movie @authorization(validate: [{ where: { id: "1" } }]) { + actors: [Actor!]! + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "@authorization" requires in a type with "@node"'); + }); + + test("should not raise when @authorization is used within the @node directive in type extension", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie @node { + id: ID! + title: String + } + + extend type Movie @authorization(validate: [{ where: { id: "1" } }]) { + actors: [Actor!]! + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow(); + }); + + test("should raise when @authorization is not used within the @node directive on field in type extension", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie { + id: ID! + title: String + } + + extend type Movie { + actors: [Actor!]! @authorization(validate: [{ where: { id: "1" } }]) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions: {}, + features: {}, + }); + expect(executeValidate).toThrow('Directive "@authorization" requires in a type with "@node"'); + }); + + test("should not raise when @authorization is used within the @node directive on field in type extension", () => { + const doc = gql` + type Actor @node { + name: String + } + + type Movie @node { + id: ID! + title: String + } + + extend type Movie { + actors: [Actor!]! @authorization(validate: [{ where: { id: "1" } }]) + } + `; + + const executeValidate = () => + validateDocument({ + document: doc, + additionalDefinitions, + features: {}, + }); + expect(executeValidate).not.toThrow(); + }); +}); diff --git a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.ts b/packages/graphql/src/schema/validation/schema-model-rules/plural-conflict.ts similarity index 53% rename from packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.ts rename to packages/graphql/src/schema/validation/schema-model-rules/plural-conflict.ts index 2cdeef9a4b..fc8765c255 100644 --- a/packages/graphql/src/classes/subscription/Neo4jGraphQLSubscriptionsDefaultEngine.ts +++ b/packages/graphql/src/schema/validation/schema-model-rules/plural-conflict.ts @@ -17,22 +17,20 @@ * limitations under the License. */ -import { EventEmitter } from "events"; -import type { Neo4jGraphQLSubscriptionsEngine, SubscriptionsEvent } from "../../types"; +import { GraphQLError } from "graphql"; +import type { Neo4jGraphQLSchemaModel } from "../../../schema-model/Neo4jGraphQLSchemaModel"; -export class Neo4jGraphQLSubscriptionsDefaultEngine implements Neo4jGraphQLSubscriptionsEngine { - public events: EventEmitter = new EventEmitter(); +export function pluralConflict(schemaModel: Neo4jGraphQLSchemaModel): GraphQLError[] { + const entities = schemaModel.entities.values(); + const errors: GraphQLError[] = []; - public closed = false; - - public publish(eventMeta: SubscriptionsEvent): void | Promise { - if (!this.closed) { - this.events.emit(eventMeta.event, eventMeta); + const plurals = new Set(); + for (const entity of entities) { + if (plurals.has(entity.plural)) { + errors.push(new GraphQLError(`Ambiguous plural "${entity.plural}" in "${entity.name}"`)); } + plurals.add(entity.plural); } - /** Stops event publishing */ - public close(): void { - this.closed = true; - } + return errors; } diff --git a/packages/graphql/src/schema/validation/schema-validation.test.ts b/packages/graphql/src/schema/validation/schema-validation.test.ts index 7461225ccc..334905178b 100644 --- a/packages/graphql/src/schema/validation/schema-validation.test.ts +++ b/packages/graphql/src/schema/validation/schema-validation.test.ts @@ -17,29 +17,29 @@ * limitations under the License. */ -import type { ASTVisitor, FieldDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { GraphQLError, parse } from "graphql"; +import type { ObjectTypeDefinitionNode } from "graphql"; +import { parse } from "graphql"; import { gql } from "graphql-tag"; -import type { SDLValidationContext } from "graphql/validation/ValidationContext"; import { NoErrorThrownError, getError } from "../../../tests/utils/get-error"; import { Subgraph } from "../../classes/Subgraph"; import { generateModel } from "../../schema-model/generate-model"; import makeAugmentedSchema from "../make-augmented-schema"; import { validateUserDefinition } from "./schema-validation"; +import { ComplexityEstimatorHelper } from "../../classes/ComplexityEstimatorHelper"; describe("schema validation", () => { describe("JWT", () => { // TODO: authentication describe("JWT Payload", () => { test("should not returns errors when is correctly used", () => { - const jwtType = ` - type MyJWT @jwt { + const jwtType = /* GraphQL */ ` + type MyJWT @jwt { myClaim: String } `; const userDocument = gql` ${jwtType} - type User @authorization(filter: [{ where: { jwt: { myClaim: "something" } } }]) @node { + type User @authorization(filter: [{ where: { jwt: { myClaim: { eq: "something" } } } }]) @node { id: ID! name: String! } @@ -50,14 +50,15 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); }); test("should not returns errors when is correctly used together with node", () => { - const jwtType = ` - type MyJWT @jwt { + const jwtType = /* GraphQL */ ` + type MyJWT @jwt { myClaim: String } `; @@ -65,7 +66,9 @@ describe("schema validation", () => { ${jwtType} type User @node - @authorization(filter: [{ where: { jwt: { myClaim: "something" }, node: { name: "John" } } }]) { + @authorization( + filter: [{ where: { jwt: { myClaim_EQ: "something" }, node: { name_EQ: "John" } } }] + ) { id: ID! name: String! } @@ -76,20 +79,23 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); }); test("should return errors when jwt field is not found", () => { - const jwtType = ` - type MyJWT @jwt { + const jwtType = /* GraphQL */ ` + type MyJWT @jwt { myClaim: String } `; const userDocument = gql` ${jwtType} - type User @node @authorization(filter: [{ where: { jwt: { thisClaimDoesNotExist: "something" } } }]) { + type User + @node + @authorization(filter: [{ where: { jwt: { thisClaimDoesNotExist_EQ: "something" } } }]) { id: ID! name: String! } @@ -100,6 +106,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -108,14 +115,14 @@ describe("schema validation", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - 'Invalid argument: filter, error: Field "thisClaimDoesNotExist" is not defined by type.' + 'Invalid argument: filter, error: Field "thisClaimDoesNotExist_EQ" is not defined by type.' ); expect(errors[0]).toHaveProperty("path", ["User", "@authorization", "filter", 0, "where", "jwt"]); }); test("should return no error when jwt field iss is used", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { jwt: { iss: "Something" } } }]) { + type User @authorization(filter: [{ where: { jwt: { iss_EQ: "Something" } } }]) { id: ID! name: String! } @@ -125,6 +132,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -138,7 +146,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @authorization(filter: [{ where: { jwt: { iss: "something" } } }]) @node { + type User @authorization(filter: [{ where: { jwt: { iss_EQ: "something" } } }]) @node { id: ID! name: String! } @@ -149,6 +157,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -162,7 +171,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @authorization(filter: [{ where: { node: { myClaim: "something" } } }]) @node { + type User @authorization(filter: [{ where: { node: { myClaim_EQ: "something" } } }]) @node { id: ID! name: String! } @@ -173,6 +182,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -181,7 +191,7 @@ describe("schema validation", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - 'Invalid argument: filter, error: Field "myClaim" is not defined by type.' + 'Invalid argument: filter, error: Field "myClaim_EQ" is not defined by type.' ); expect(errors[0]).toHaveProperty("path", ["User", "@authorization", "filter", 0, "where", "node"]); }); @@ -207,6 +217,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -220,7 +231,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testInt: "$jwt.intClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testInt_EQ: "$jwt.intClaim" } } }]) { id: ID! name: String! testInt: Int @@ -232,6 +243,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -239,7 +251,7 @@ describe("schema validation", () => { test("should not returns errors when is correctly used: standard field sub on OBJECT", () => { const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testStr: "$jwt.sub" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testStr_EQ: "$jwt.sub" } } }]) { id: ID! name: String! testStr: String @@ -250,6 +262,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -262,7 +275,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testInt: "$jwt.intClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testInt_EQ: "$jwt.intClaim" } } }]) { id: ID! name: String! testInt: [Int] @@ -274,6 +287,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -286,7 +300,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testBool: "$jwt.boolClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testBool_EQ: "$jwt.boolClaim" } } }]) { id: ID! name: String! testBool: Boolean @@ -298,6 +312,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -305,7 +320,7 @@ describe("schema validation", () => { test("should return error when types do not match: standard field sub on OBJECT", () => { const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testInt: "$jwt.sub" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testInt_EQ: "$jwt.sub" } } }]) { id: ID! name: String! testInt: Int @@ -316,6 +331,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); const errors = getError(executeValidate); @@ -332,7 +348,7 @@ describe("schema validation", () => { 0, "where", "node", - "testInt", + "testInt_EQ", ]); }); @@ -343,7 +359,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testInts: "$jwt.intClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testInts_EQ: "$jwt.intClaim" } } }]) { id: ID! name: String! testInts: Int @@ -355,6 +371,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); const errors = getError(executeValidate); @@ -371,7 +388,7 @@ describe("schema validation", () => { 0, "where", "node", - "testInts", + "testInts_EQ", ]); }); @@ -382,7 +399,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testInt: "$jwt.stringClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testInt_EQ: "$jwt.stringClaim" } } }]) { id: ID! name: String! testInt: Int @@ -394,6 +411,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); const errors = getError(executeValidate); @@ -410,7 +428,7 @@ describe("schema validation", () => { 0, "where", "node", - "testInt", + "testInt_EQ", ]); }); @@ -422,7 +440,7 @@ describe("schema validation", () => { `; const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { testInt: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { node: { testInt_EQ: "$jwt.sub" } } }]) name: String! testInt: Int } @@ -433,6 +451,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -452,7 +471,7 @@ describe("schema validation", () => { 0, "where", "node", - "testInt", + "testInt_EQ", ]); }); @@ -464,7 +483,7 @@ describe("schema validation", () => { `; const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { testStr: "$jwt.intClaim" } } }]) + id: ID! @authorization(filter: [{ where: { node: { testStr_EQ: "$jwt.intClaim" } } }]) name: String! testStr: String } @@ -475,6 +494,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -494,7 +514,7 @@ describe("schema validation", () => { 0, "where", "node", - "testStr", + "testStr_EQ", ]); }); @@ -506,7 +526,7 @@ describe("schema validation", () => { `; const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { testStr: "$jwt.intClaim" } } }]) + id: ID! @authorization(filter: [{ where: { node: { testStr_EQ: "$jwt.intClaim" } } }]) name: String! testStr: String } @@ -517,6 +537,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -536,7 +557,7 @@ describe("schema validation", () => { 0, "where", "node", - "testStr", + "testStr_EQ", ]); }); @@ -548,7 +569,7 @@ describe("schema validation", () => { `; const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { testStr: "$jwt.boolClaim" } } }]) + id: ID! @authorization(filter: [{ where: { node: { testStr_EQ: "$jwt.boolClaim" } } }]) name: String! testStr: String } @@ -559,6 +580,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -578,7 +600,7 @@ describe("schema validation", () => { 0, "where", "node", - "testStr", + "testStr_EQ", ]); }); @@ -589,7 +611,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testBool: "$jwt.stringClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testBool_EQ: "$jwt.stringClaim" } } }]) { id: ID! name: String! testBool: Boolean @@ -601,6 +623,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -618,7 +641,7 @@ describe("schema validation", () => { 0, "where", "node", - "testBool", + "testBool_EQ", ]); }); @@ -629,7 +652,7 @@ describe("schema validation", () => { } `; const userDocument = gql` - type User @node @authorization(filter: [{ where: { node: { testBool: "$jwt.intClaim" } } }]) { + type User @node @authorization(filter: [{ where: { node: { testBool_EQ: "$jwt.intClaim" } } }]) { id: ID! name: String! testBool: Boolean @@ -641,6 +664,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -658,7 +682,7 @@ describe("schema validation", () => { 0, "where", "node", - "testBool", + "testBool_EQ", ]); }); }); @@ -668,7 +692,7 @@ describe("schema validation", () => { describe("on OBJECT", () => { test("should not returns errors when is correctly used", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -679,6 +703,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -686,7 +711,7 @@ describe("schema validation", () => { test("should not returns errors when is correctly used, with specifiedDirective", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! @deprecated(reason: "name is deprecated") } @@ -697,6 +722,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -704,12 +730,12 @@ describe("schema validation", () => { test("should not returns errors when used correctly in several place", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } - type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -720,6 +746,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -727,7 +754,9 @@ describe("schema validation", () => { test("should validate directive argument name", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User + @node + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -738,6 +767,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); // @@ -760,7 +790,7 @@ describe("schema validation", () => { type User @node @plural(value: "Users") - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -771,6 +801,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -782,7 +813,7 @@ describe("schema validation", () => { type User @node @plural(value: "Users") - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -793,6 +824,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -815,7 +847,7 @@ describe("schema validation", () => { test("should not returns errors with a correct usage", () => { const userDocument = gql` type User @node { - id: ID! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -825,6 +857,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -834,7 +867,7 @@ describe("schema validation", () => { test("should validate directive argument name", () => { const userDocument = gql` type User @node { - id: ID! @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -844,6 +877,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -868,7 +902,7 @@ describe("schema validation", () => { name: String! posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Post @node { @@ -881,6 +915,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -893,7 +928,7 @@ describe("schema validation", () => { name: String! posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Post @node { @@ -906,6 +941,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -932,12 +968,12 @@ describe("schema validation", () => { name: String! } - interface Member @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + interface Member @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! } type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -946,6 +982,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).toThrow( @@ -961,7 +998,7 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -969,6 +1006,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -976,7 +1014,7 @@ describe("schema validation", () => { test("should returns errors when used correctly in both type and extension", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -985,7 +1023,7 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -993,6 +1031,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1009,7 +1048,7 @@ describe("schema validation", () => { const userDocument = gql` type User @node { id: ID! - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Post @node { @@ -1017,7 +1056,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -1026,6 +1065,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1040,7 +1080,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in both type and an extension field", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1050,7 +1090,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -1059,6 +1099,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1066,7 +1107,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in multiple extension fields", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1076,8 +1117,8 @@ describe("schema validation", () => { name: String! } extend type User { - id: ID! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -1086,6 +1127,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1093,7 +1135,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in different type and field across several extensions", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1104,11 +1146,11 @@ describe("schema validation", () => { } extend type User { - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } extend type User { - id: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -1117,25 +1159,26 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); }); - test("should returns errors when used correctly in more than one extension", () => { + test("should returns error when used correctly in more than one extension", () => { const userDocument = gql` type User @node { id: ID! name: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Post @node { id: ID! name: String! } - extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1143,6 +1186,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1161,7 +1205,8 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1169,6 +1214,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1194,7 +1240,7 @@ describe("schema validation", () => { } extend type User @plural(value: "Users") - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1202,6 +1248,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1215,7 +1262,7 @@ describe("schema validation", () => { } extend type User @plural(value: "Users") - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1223,6 +1270,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1253,10 +1301,10 @@ describe("schema validation", () => { id: ID! } extend interface Member - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -1265,6 +1313,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).toThrow( @@ -1276,17 +1325,17 @@ describe("schema validation", () => { describe("mixed usage", () => { test("should not returns errors when used correctly in several place", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } - type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! - name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - author: User! + name: String! @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Document implements File @node { @@ -1299,7 +1348,7 @@ describe("schema validation", () => { } extend type Document - @subscriptionsAuthorization(filter: [{ where: { node: { name: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { name_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1307,24 +1356,25 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); }); test("should returns errors when incorrectly used in several place", () => { const userDocument = gql` - type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type User @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } - type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) - author: User! + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Document implements File @node { @@ -1337,7 +1387,7 @@ describe("schema validation", () => { } extend type Document - @subscriptionsAuthorization(filter: [{ where: { node: { name: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { node: { name_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); @@ -1345,6 +1395,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1371,14 +1422,14 @@ describe("schema validation", () => { type User @node @shareable - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1388,6 +1439,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1406,14 +1458,14 @@ describe("schema validation", () => { type User @node @shareable - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! @deprecated(reason: "name is deprecated") } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1423,6 +1475,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1441,14 +1494,16 @@ describe("schema validation", () => { type User @node @shareable - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } - type Post @node @subscriptionsAuthorization(filter: [{ where: { node: { content: "$jwt.sub" } } }]) { + type Post + @node + @subscriptionsAuthorization(filter: [{ where: { node: { content_EQ: "$jwt.sub" } } }]) { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1458,6 +1513,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1476,14 +1532,14 @@ describe("schema validation", () => { type User @node @shareable - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1493,6 +1549,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1524,14 +1581,14 @@ describe("schema validation", () => { @node @plural(value: "Users") @shareable - @subscriptionsAuthorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1541,6 +1598,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1560,14 +1618,14 @@ describe("schema validation", () => { @node @plural(value: "Users") @shareable - @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @subscriptionsAuthorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -1577,6 +1635,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -1606,7 +1665,7 @@ describe("schema validation", () => { describe("on OBJECT", () => { test("should not returns errors when is correctly used", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -1616,6 +1675,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1623,7 +1683,7 @@ describe("schema validation", () => { test("should not returns errors when is correctly used, with specifiedDirective", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! @deprecated(reason: "name is deprecated") } @@ -1633,6 +1693,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1640,12 +1701,12 @@ describe("schema validation", () => { test("should not returns errors when used correctly in several place", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } - type Post @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type Post @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -1655,6 +1716,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1662,7 +1724,7 @@ describe("schema validation", () => { test("should validate directive argument name", () => { const userDocument = gql` - type User @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -1672,6 +1734,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1687,7 +1750,7 @@ describe("schema validation", () => { test("should validate operations value", () => { const userDocument = gql` type User - @authorization(filter: [{ operations: [NEVER], where: { node: { id: "$jwt.sub" } } }]) + @authorization(filter: [{ operations: [NEVER], where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! @@ -1698,6 +1761,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1716,7 +1780,7 @@ describe("schema validation", () => { type User @node @plural(value: "Users") - @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1726,6 +1790,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1737,7 +1802,7 @@ describe("schema validation", () => { type User @node @plural(value: "Users") - @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } @@ -1747,6 +1812,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1764,7 +1830,7 @@ describe("schema validation", () => { test("should not returns errors with a correct usage", () => { const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -1773,6 +1839,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1782,7 +1849,7 @@ describe("schema validation", () => { test("should validate directive argument name", () => { const userDocument = gql` type User @node { - id: ID! @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -1791,6 +1858,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1807,7 +1875,7 @@ describe("schema validation", () => { test("should validate when value", () => { const userDocument = gql` type User @node { - id: ID! @authorization(validate: [{ when: [NEVER], where: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(validate: [{ when: [NEVER], where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -1816,6 +1884,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1833,7 +1902,9 @@ describe("schema validation", () => { const userDocument = gql` type User @node { id: ID! - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @authentication + name: String! + @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + @authentication posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) } @@ -1846,6 +1917,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1857,7 +1929,7 @@ describe("schema validation", () => { type User @node { id: ID! name: String! - @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @authentication posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) } @@ -1871,6 +1943,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1893,12 +1966,12 @@ describe("schema validation", () => { name: String! } - interface Member @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + interface Member @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! } type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!] @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -1906,6 +1979,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1923,13 +1997,14 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -1937,7 +2012,7 @@ describe("schema validation", () => { test("should returns errors when used correctly in both type and extension", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -1946,13 +2021,14 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1969,7 +2045,7 @@ describe("schema validation", () => { const userDocument = gql` type User @node { id: ID! - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } type Post @node { @@ -1977,7 +2053,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -1985,6 +2061,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -1999,7 +2076,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in both type and an extension field", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -2009,7 +2086,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -2017,6 +2094,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -2024,7 +2102,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in multiple extension fields", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -2034,8 +2112,8 @@ describe("schema validation", () => { name: String! } extend type User { - id: ID! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -2043,6 +2121,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -2050,7 +2129,7 @@ describe("schema validation", () => { test("should not returns errors when used correctly in different type and field across several extensions", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -2061,11 +2140,11 @@ describe("schema validation", () => { } extend type User { - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } extend type User { - id: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) } `; @@ -2073,6 +2152,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -2083,21 +2163,22 @@ describe("schema validation", () => { type User @node { id: ID! name: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Post @node { id: ID! name: String! } - extend type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - extend type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2116,13 +2197,14 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend type User @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2143,13 +2225,14 @@ describe("schema validation", () => { } extend type User @plural(value: "Users") - @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2164,13 +2247,14 @@ describe("schema validation", () => { } extend type User @plural(value: "Users") - @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) + @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); const errors = getError(executeValidate); @@ -2194,10 +2278,10 @@ describe("schema validation", () => { interface Member { id: ID! } - extend interface Member @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + extend interface Member @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -2205,6 +2289,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2218,15 +2303,15 @@ describe("schema validation", () => { describe("mixed usage", () => { test("should not returns errors when used correctly in several place", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } - type Post @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type Post @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! - name: String! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + name: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Document implements File @node { @@ -2238,28 +2323,29 @@ describe("schema validation", () => { name: String } - extend type Document @authorization(filter: [{ where: { node: { name: "$jwt.sub" } } }]) + extend type Document @authorization(filter: [{ where: { node: { name_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); }); test("should returns errors when incorrectly used in several place", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } - type Post @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type Post @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! - name: String! @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + name: String! @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Document implements File @node { @@ -2271,13 +2357,14 @@ describe("schema validation", () => { name: String } - extend type Document @authorization(filter: [{ where: { node: { name: "$jwt.sub" } } }]) + extend type Document @authorization(filter: [{ where: { node: { name_EQ: "$jwt.sub" } } }]) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); const errors = getError(executeValidate); @@ -2295,14 +2382,14 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @shareable @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @shareable @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2311,6 +2398,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2327,14 +2415,14 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @shareable @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @shareable @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! @deprecated(reason: "name is deprecated") } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2343,6 +2431,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2359,14 +2448,14 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @shareable @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @shareable @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } - type Post @authorization(filter: [{ where: { node: { content: "$jwt.sub" } } }]) @node { + type Post @authorization(filter: [{ where: { node: { content_EQ: "$jwt.sub" } } }]) @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2375,6 +2464,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2391,14 +2481,18 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User + @node + @shareable + @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + @node { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2407,6 +2501,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2434,14 +2529,14 @@ describe("schema validation", () => { @node @plural(value: "Users") @shareable - @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2450,6 +2545,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2470,14 +2566,14 @@ describe("schema validation", () => { @node @plural(value: "Users") @shareable - @authorization(wrongFilter: [{ where: { node: { id: "$jwt.sub" } } }]) { + @authorization(wrongFilter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -2486,6 +2582,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -2521,6 +2618,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); expect(executeValidate).not.toThrow(); @@ -2534,7 +2632,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! @deprecated(reason: "name is deprecated") } @@ -2544,6 +2642,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -2557,12 +2656,12 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } - type Post @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type Post @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2572,6 +2671,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -2589,6 +2689,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2610,6 +2711,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2631,7 +2733,10 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @plural(value: "Users") @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User + @node + @plural(value: "Users") + @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2641,6 +2746,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -2655,7 +2761,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @plural(value: "Users") @authentication(jwtWrongField: { sub: "test" }) { + type User @node @plural(value: "Users") @authentication(jwtWrongField: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2665,6 +2771,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -2691,6 +2798,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2709,6 +2817,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2731,6 +2840,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2744,36 +2854,13 @@ describe("schema validation", () => { expect(errors[0]).toHaveProperty("path", ["User", "@authentication", "operations", 0]); }); - test("validation should works when used with other directives", () => { - const userDocument = gql` - type User @node { - id: ID! @authentication(operations: [CREATE]) @unique - name: String! - posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) - } - - type Post @node { - id: ID! - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - - const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); - expect(executeValidate).not.toThrow(); - }); - test("should validate directive argument name, when used with other directives", () => { const userDocument = gql` type User @node { id: ID! name: String! - @authorization(validate: [{ where: { node: { id: "1" } } }]) - @authentication(wrongFieldName: { sub: "test" }) + @authorization(validate: [{ where: { node: { id_EQ: "1" } } }]) + @authentication(wrongFieldName: { sub_EQ: "test" }) posts: [Post!]! @relationship(type: "HAS_POSTS", direction: IN) } @@ -2786,6 +2873,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -2814,12 +2902,12 @@ describe("schema validation", () => { name: String! } - interface Member @authentication(operations: [CREATE], jwt: { myClaim: "test" }) { + interface Member @authentication(operations: [CREATE], jwt: { myClaim_EQ: "test" }) { id: ID! } type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -2829,6 +2917,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -2856,7 +2945,7 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend type User @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -2864,6 +2953,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -2878,7 +2968,7 @@ describe("schema validation", () => { const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2887,7 +2977,7 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend type User @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -2896,6 +2986,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -2918,7 +3009,7 @@ describe("schema validation", () => { ${jwtType} type User @node { id: ID! - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } type Post @node { @@ -2926,7 +3017,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } `; @@ -2935,6 +3026,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -2955,7 +3047,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2965,7 +3057,7 @@ describe("schema validation", () => { name: String! } extend type User { - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } `; @@ -2974,6 +3066,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -2987,7 +3080,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -2997,8 +3090,8 @@ describe("schema validation", () => { name: String! } extend type User { - id: ID! @authentication(operations: [CREATE], jwt: { sub: "test" }) - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + id: ID! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } `; @@ -3007,6 +3100,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -3020,7 +3114,7 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } @@ -3031,11 +3125,11 @@ describe("schema validation", () => { } extend type User { - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } extend type User { - id: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) + id: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) } `; @@ -3044,6 +3138,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -3054,7 +3149,7 @@ describe("schema validation", () => { type User @node { id: ID! name: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Post @node { @@ -3069,6 +3164,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -3094,6 +3190,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -3115,13 +3212,16 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @plural(value: "Users") @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend type User + @plural(value: "Users") + @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -3134,13 +3234,14 @@ describe("schema validation", () => { id: ID! name: String! } - extend type User @plural(value: "Users") @authentication(wrongField: { sub: "test" }) + extend type User @plural(value: "Users") @authentication(wrongField: { sub_EQ: "test" }) `; const schemaModel = generateModel(userDocument); const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); const errors = getError(executeValidate); @@ -3170,10 +3271,10 @@ describe("schema validation", () => { interface Member { id: ID! } - extend interface Member @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend interface Member @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) type Post @node { - author: Member @relationship(type: "HAS_AUTHOR", direction: IN) + author: [Member!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -3182,6 +3283,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); @@ -3204,15 +3306,15 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } - type Post @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type Post @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! - name: String! @authentication(operations: [CREATE], jwt: { sub: "test" }) - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + name: String! @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Document implements File @node { @@ -3224,7 +3326,7 @@ describe("schema validation", () => { name: String } - extend type Document @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend type Document @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3232,6 +3334,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); expect(executeValidate).not.toThrow(); @@ -3244,15 +3347,15 @@ describe("schema validation", () => { `; const userDocument = gql` ${jwtType} - type User @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } - type Post @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type Post @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! - name: String! @authentication(ops: [CREATE], jwt: { sub: "test" }) - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + name: String! @authentication(ops: [CREATE], jwt: { sub_EQ: "test" }) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } type Document implements File @node { @@ -3264,7 +3367,7 @@ describe("schema validation", () => { name: String } - extend type Document @authentication(operations: [CREATE], jwt: { sub: "test" }) + extend type Document @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3272,6 +3375,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument, jwt }); const errors = getError(executeValidate); @@ -3292,14 +3396,14 @@ describe("schema validation", () => { ${jwtType} extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @shareable @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3309,6 +3413,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3332,14 +3437,14 @@ describe("schema validation", () => { ${jwtType} extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @shareable @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! @deprecated(reason: "name is deprecated") } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3349,6 +3454,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3372,14 +3478,14 @@ describe("schema validation", () => { ${jwtType} extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type User @node @shareable @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } - type Post @node @authentication(operations: [CREATE], jwt: { sub: "test" }) { + type Post @node @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3389,6 +3495,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3406,14 +3513,14 @@ describe("schema validation", () => { const userDocument = gql` extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) - type User @node @shareable @authentication(wrongField: { sub: "test" }) { + type User @node @shareable @authentication(wrongField: { sub_EQ: "test" }) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const subgraph = new Subgraph(userDocument); @@ -3422,6 +3529,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3455,14 +3563,14 @@ describe("schema validation", () => { @node @plural(value: "Users") @shareable - @authentication(operations: [CREATE], jwt: { sub: "test" }) { + @authentication(operations: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3472,6 +3580,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3495,14 +3604,18 @@ describe("schema validation", () => { extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@shareable"]) ${jwtType} - type User @node @plural(value: "Users") @shareable @authentication(ops: [CREATE], jwt: { sub: "test" }) { + type User + @node + @plural(value: "Users") + @shareable + @authentication(ops: [CREATE], jwt: { sub_EQ: "test" }) { id: ID! name: String! } type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; const jwt = parse(jwtType).definitions[0] as ObjectTypeDefinitionNode; @@ -3512,6 +3625,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => @@ -3531,69 +3645,12 @@ describe("schema validation", () => { }); }); - describe("validate using custom rules", () => { - test("should not returns errors when is correctly used", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - rules: [noKeanuFields], - }); - expect(executeValidate).not.toThrow(); - }); - test("should returns errors when is not correctly used", () => { - const userDocument = gql` - type User @node { - id: ID! - keanu: String! - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - rules: [noKeanuFields], - }); - - const errors = getError(executeValidate); - expect(errors).toHaveLength(2); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "Field cannot be named keanu"); - expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[1]).toHaveProperty("message", "Field cannot be named keanu"); - }); - }); - describe("input validation", () => { describe("on OBJECT", () => { describe("correct usage", () => { test("should not returns errors with a valid @authorization filter argument", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -3603,6 +3660,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3626,64 +3684,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - expect(executeValidate).not.toThrow(); - }); - - test("should no returns errors when an @authorization filter has a correct where predicate over a 1 to 1 relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @authorization(filter: [{ where: { node: { author: { name: "Simone" } } } }]) @node { - content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - expect(executeValidate).not.toThrow(); - }); - - test("should no returns errors when an @authorization filter has a correct where predicate over a 1 to N relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post - @authorization(filter: [{ where: { node: { authors_SOME: { name: "Simone" } } } }]) - @node { - content: String! - authors: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3699,7 +3700,9 @@ describe("schema validation", () => { describe("incorrect usage", () => { test("should returns errors when an @authorization filter contains an unknown operation", () => { const userDocument = gql` - type User @authorization(filter: [{ seemsNotAWhereToMe: { node: { id: "$jwt.sub" } } }]) @node { + type User + @authorization(filter: [{ seemsNotAWhereToMe: { node: { id_EQ: "$jwt.sub" } } }]) + @node { id: ID! name: String! } @@ -3709,6 +3712,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3732,8 +3736,8 @@ describe("schema validation", () => { type User @node { name: String! id: ID! - @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) - @subscriptionsAuthorization(filter: [{ where: { wrongNode: { id: "$jwt.sub" } } }]) + @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + @subscriptionsAuthorization(filter: [{ where: { wrongNode: { id_EQ: "$jwt.sub" } } }]) } `; @@ -3742,6 +3746,7 @@ describe("schema validation", () => { document: userDocument, features: { subscriptions: true }, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ userDocument, augmentedDocument }); @@ -3757,7 +3762,7 @@ describe("schema validation", () => { test("should returns errors when an @authorization filter has a wrong where definition", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { notANode: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { notANode: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -3767,6 +3772,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3787,7 +3793,7 @@ describe("schema validation", () => { test("should returns errors when an @authorization filter has a wrong where predicate", () => { const userDocument = gql` - type User @authorization(filter: [{ where: { node: { notAValidID: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { notAValidID_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! } @@ -3797,6 +3803,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3810,93 +3817,10 @@ describe("schema validation", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - 'Invalid argument: filter, error: Field "notAValidID" is not defined by type.' + 'Invalid argument: filter, error: Field "notAValidID_EQ" is not defined by type.' ); expect(errors[0]).toHaveProperty("path", ["User", "@authorization", "filter", 0, "where", "node"]); }); - - test("should returns errors when an @authorization filter has an incorrect where predicate over a 1 to 1 relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post - @authorization(filter: [{ where: { node: { author: { content: "Simone" } } } }]) - @node { - content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - const errors = getError(executeValidate); - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - 'Invalid argument: filter, error: Field "content" is not defined by type.' - ); - expect(errors[0]).toHaveProperty("path", [ - "Post", - "@authorization", - "filter", - 0, - "where", - "node", - "author", - ]); - }); - - test("should returns errors when an @authorization filter has an incorrect where predicate over a 1 to N relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @node - @authorization( - filter: [{ where: { node: { author_NOT_A_QUANTIFIER: { name: "Simone" } } } }] - ) { - content: String! - authors: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - const errors = getError(executeValidate); - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - 'Invalid argument: filter, error: Field "author_NOT_A_QUANTIFIER" is not defined by type.' - ); - expect(errors[0]).toHaveProperty("path", ["Post", "@authorization", "filter", 0, "where", "node"]); - }); }); }); @@ -3905,7 +3829,7 @@ describe("schema validation", () => { test("should not returns errors with a valid @authorization filter argument", () => { const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -3914,6 +3838,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -3937,64 +3862,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - expect(executeValidate).not.toThrow(); - }); - - test("should no returns errors when an @authorization filter has a correct where predicate over a 1 to 1 relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @node { - content: String! - @authorization(filter: [{ where: { node: { author: { name: "Simone" } } } }]) - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - expect(executeValidate).not.toThrow(); - }); - - test("should no returns errors when an @authorization filter has a correct where predicate over a 1 to N relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @node { - content: String! - @authorization(filter: [{ where: { node: { authors_SOME: { name: "Simone" } } } }]) - authors: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -4011,7 +3879,7 @@ describe("schema validation", () => { test("should returns errors when an @authorization filter contains an unknown operation", () => { const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ seemsNotAWhereToMe: { node: { id: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ seemsNotAWhereToMe: { node: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -4020,6 +3888,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -4041,7 +3910,7 @@ describe("schema validation", () => { test("should returns errors when an @authorization filter has a wrong where definition", () => { const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { notANode: { id: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { notANode: { id_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -4050,6 +3919,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -4071,7 +3941,7 @@ describe("schema validation", () => { test("should returns errors when an @authorization filter has a wrong where predicate", () => { const userDocument = gql` type User @node { - id: ID! @authorization(filter: [{ where: { node: { notAValidID: "$jwt.sub" } } }]) + id: ID! @authorization(filter: [{ where: { node: { notAValidID_EQ: "$jwt.sub" } } }]) name: String! } `; @@ -4080,6 +3950,7 @@ describe("schema validation", () => { const { typeDefs: augmentedDocument } = makeAugmentedSchema({ document: userDocument, schemaModel, + complexityEstimatorHelper: new ComplexityEstimatorHelper(false), }); const executeValidate = () => validateUserDefinition({ @@ -4093,7 +3964,7 @@ describe("schema validation", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - 'Invalid argument: filter, error: Field "notAValidID" is not defined by type.' + 'Invalid argument: filter, error: Field "notAValidID_EQ" is not defined by type.' ); expect(errors[0]).toHaveProperty("path", [ "User", @@ -4105,109 +3976,7 @@ describe("schema validation", () => { "node", ]); }); - - test("should returns errors when an @authorization filter has an incorrect where predicate over a 1 to 1 relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @node { - content: String! - @authorization(filter: [{ where: { node: { author: { content: "Simone" } } } }]) - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - const errors = getError(executeValidate); - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - 'Invalid argument: filter, error: Field "content" is not defined by type.' - ); - expect(errors[0]).toHaveProperty("path", [ - "Post", - "content", - "@authorization", - "filter", - 0, - "where", - "node", - "author", - ]); - }); - - test("should returns errors when an @authorization filter has an incorrect where predicate over a 1 to N relationship", () => { - const userDocument = gql` - type User @node { - id: ID! - name: String! - } - - type Post @node { - content: String! - @authorization( - filter: [{ where: { node: { author_NOT_A_QUANTIFIER: { name: "Simone" } } } }] - ) - authors: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - `; - - const schemaModel = generateModel(userDocument); - const { typeDefs: augmentedDocument } = makeAugmentedSchema({ - document: userDocument, - schemaModel, - }); - const executeValidate = () => - validateUserDefinition({ - userDocument, - augmentedDocument, - additionalDirectives: [], - additionalTypes: [], - }); - - const errors = getError(executeValidate); - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - 'Invalid argument: filter, error: Field "author_NOT_A_QUANTIFIER" is not defined by type.' - ); - expect(errors[0]).toHaveProperty("path", [ - "Post", - "content", - "@authorization", - "filter", - 0, - "where", - "node", - ]); - }); }); }); }); }); - -function noKeanuFields(context: SDLValidationContext): ASTVisitor { - return { - FieldDefinition(node: FieldDefinitionNode) { - if (node.name.value === "keanu") { - context.reportError(new GraphQLError("Field cannot be named keanu")); - } - }, - }; -} diff --git a/packages/graphql/src/schema/validation/schema-validation.ts b/packages/graphql/src/schema/validation/schema-validation.ts index da2e916ae7..946c3a3a9e 100644 --- a/packages/graphql/src/schema/validation/schema-validation.ts +++ b/packages/graphql/src/schema/validation/schema-validation.ts @@ -21,17 +21,43 @@ import type { DefinitionNode, DocumentNode, GraphQLDirective, + GraphQLInputObjectType, GraphQLNamedType, ObjectTypeDefinitionNode, } from "graphql"; import { GraphQLSchema, specifiedDirectives, visit } from "graphql"; -import type { SDLValidationRule } from "graphql/validation/ValidationContext"; import { specifiedSDLRules } from "graphql/validation/specifiedRules"; import { createAuthenticationDirectiveDefinition } from "../../graphql/directives/type-dependant-directives/authentication"; import { getStaticAuthorizationDefinitions } from "../../graphql/directives/type-dependant-directives/get-static-auth-definitions"; +import { + BigIntListFilters, + BigIntScalarFilters, +} from "../../graphql/input-objects/generic-operators/BigIntScalarFilters"; +import { + BooleanListFilters, + BooleanScalarFilters, +} from "../../graphql/input-objects/generic-operators/BooleanScalarFilters"; +import { + CartesianPointFilters, + CartesianPointListFilters, +} from "../../graphql/input-objects/generic-operators/CartesianPointFilters"; +import { DateListFilters, DateScalarFilters } from "../../graphql/input-objects/generic-operators/DateScalarFilters"; +import { DateTimeScalarFilters } from "../../graphql/input-objects/generic-operators/DateTimeScalarFilters"; +import { DurationScalarFilters } from "../../graphql/input-objects/generic-operators/DurationScalarFilters"; +import { FloatListFilters, FloatScalarFilters } from "../../graphql/input-objects/generic-operators/FloatScalarFilters"; +import { getIDScalarFilters, IDListFilters } from "../../graphql/input-objects/generic-operators/IDScalarFilters"; +import { IntListFilters, IntScalarFilters } from "../../graphql/input-objects/generic-operators/IntScalarFilters"; +import { LocalTimeScalarFilters } from "../../graphql/input-objects/generic-operators/LocalTimeScalarFilters"; +import { PointFilters, PointListFilters } from "../../graphql/input-objects/generic-operators/PointFilters"; +import { + getStringScalarFilters, + StringListFilters, +} from "../../graphql/input-objects/generic-operators/StringScalarFilters"; +import { TimeScalarFilters } from "../../graphql/input-objects/generic-operators/TimeScalarFilters"; +import type { Neo4jFeaturesSettings } from "../../types"; import { EnricherContext } from "./EnricherContext"; -import { DirectiveArgumentOfCorrectType } from "./custom-rules/directive-argument-of-correct-type"; import { makeReplaceWildcardVisitor } from "./custom-rules/replace-wildcard-value"; +import { ValidateAuthorizationLikeDirectives } from "./custom-rules/validate-authorization-like-directives"; import { authenticationDirectiveEnricher } from "./enrichers/authentication"; import { authorizationDefinitionsEnricher, authorizationDirectiveEnricher } from "./enrichers/authorization"; import { @@ -82,25 +108,50 @@ export function validateUserDefinition({ augmentedDocument, additionalDirectives = [], additionalTypes = [], - rules, jwt, + features, }: { userDocument: DocumentNode; augmentedDocument: DocumentNode; additionalDirectives?: Array; additionalTypes?: Array; - rules?: readonly SDLValidationRule[]; jwt?: ObjectTypeDefinitionNode; + features?: Neo4jFeaturesSettings; }): void { - rules = rules ? rules : [...specifiedSDLRules, DirectiveArgumentOfCorrectType()]; + const rules = [...specifiedSDLRules, ValidateAuthorizationLikeDirectives]; let validationDocument = makeValidationDocument(userDocument, augmentedDocument, jwt); - + const genericFiltersName: GraphQLInputObjectType[] = [ + BooleanScalarFilters, + BooleanListFilters, + getIDScalarFilters(features), + IDListFilters, + getStringScalarFilters(features), + StringListFilters, + FloatScalarFilters, + FloatListFilters, + IntScalarFilters, + IntListFilters, + BigIntScalarFilters, + BigIntListFilters, + TimeScalarFilters, + DateTimeScalarFilters, + DateScalarFilters, + DateListFilters, + DurationScalarFilters, + LocalTimeScalarFilters, + PointFilters, + PointListFilters, + CartesianPointFilters, + CartesianPointListFilters, + ]; const schemaToExtend = new GraphQLSchema({ directives: [...specifiedDirectives, ...additionalDirectives], - types: [...additionalTypes], + types: [...genericFiltersName, ...additionalTypes], }); const replaceWildcardValue = makeReplaceWildcardVisitor({ jwt, schema: schemaToExtend }); + + validationDocument = removeDuplicateTypes(validationDocument, schemaToExtend.getTypeMap()); validationDocument = visit(validationDocument, replaceWildcardValue()); const errors = validateSDL(validationDocument, rules, schemaToExtend); @@ -108,3 +159,18 @@ export function validateUserDefinition({ throw errors; } } + +function removeDuplicateTypes(doc: DocumentNode, extraTypes: Record): DocumentNode { + return { + ...doc, + // Remove duplicate types generated by genericFiltersName + definitions: doc.definitions.filter((def) => { + if (def["name"] !== undefined) { + if (extraTypes[(def as any).name.value]) { + return false; + } + } + return true; + }), + }; +} diff --git a/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts b/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts index a8701517b0..0688d03824 100644 --- a/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts +++ b/packages/graphql/src/schema/validation/utils/invalid-directive-combinations.ts @@ -40,12 +40,11 @@ export const invalidFieldCombinations: InvalidFieldCombinations = { "subscriptionsAuthorization", "id", "relationship", - "unique", "filterable", "settable", "selectable", ], - cypher: ["jwtClaim", "alias", "id", "relationship", "unique"], + cypher: ["alias", "id", "relationship"], default: ["jwtClaim", "populatedBy", "relationship"], id: ["jwtClaim", "cypher", "populatedBy", "customResolver", "relationship", "timestamp"], populatedBy: ["jwtClaim", "id", "default", "relationship"], @@ -61,11 +60,9 @@ export const invalidFieldCombinations: InvalidFieldCombinations = { "id", "customResolver", "populatedBy", - "unique", ], - timestamp: ["jwtClaim", "id", "unique"], - unique: ["jwtClaim", "cypher", "customResolver", "relationship", "timestamp"], - jwtClaim: FIELD_DIRECTIVES, + timestamp: ["jwtClaim", "id"], + jwtClaim: FIELD_DIRECTIVES.filter((directive) => directive !== "cypher"), relayId: ["jwtClaim"], subscriptionsAuthorization: ["jwtClaim", "customResolver", "relationship"], selectable: ["jwtClaim", "customResolver"], diff --git a/packages/graphql/src/schema/validation/utils/map-error.ts b/packages/graphql/src/schema/validation/utils/map-error.ts index ca1c9cc040..6dcdadf414 100644 --- a/packages/graphql/src/schema/validation/utils/map-error.ts +++ b/packages/graphql/src/schema/validation/utils/map-error.ts @@ -18,9 +18,9 @@ */ import type { ArgumentNode, DirectiveNode, GraphQLError } from "graphql"; -import { VALIDATION_ERROR_CODES } from "./validation-error-codes"; import { lowerFirst } from "../../../utils/lower-first"; import { createGraphQLError } from "../custom-rules/utils/document-validation-error"; +import { VALIDATION_ERROR_CODES } from "./validation-error-codes"; export function mapError(error: GraphQLError): GraphQLError { const { nodes, message } = error; @@ -34,6 +34,7 @@ export function mapError(error: GraphQLError): GraphQLError { } return createGraphQLError({ nodes, + path: error.path, errorMsg: replacedMessage, }); } diff --git a/packages/graphql/src/schema/validation/validate-document.test.ts b/packages/graphql/src/schema/validation/validate-document.test.ts index cede1ca84e..29c6ee6101 100644 --- a/packages/graphql/src/schema/validation/validate-document.test.ts +++ b/packages/graphql/src/schema/validation/validate-document.test.ts @@ -74,93 +74,159 @@ describe("authorization warning", () => { }); }); -describe("list of lists warning", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); - - afterEach(() => { - warn.mockReset(); +describe("valid list fields in @node and @relationshipProperties types", () => { + test("should raise when a list of nullable elements is found", () => { + const doc = gql` + type Movie @node { + id: [ID] + } + `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@node" types. Found: [ID]' + ); + expect(errors[0]).toHaveProperty("path", ["Movie", "id"]); }); - test("list of lists warning only occurs once for multiple fields", () => { + test("should raise when a list of nullable elements is found on relationship properties", () => { + const relationshipProps = gql` + type ActedIn @relationshipProperties { + roles: [String] + } + `; const doc = gql` + ${relationshipProps} type Movie @node { - id: [[ID]] + title: String } type Actor @node { - name: [[String]] + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; + const objects = relationshipProps.definitions as ObjectTypeDefinitionNode[]; + const executeValidate = () => + validateDocument({ + document: doc, + features: {}, + additionalDefinitions: { enums: [], interfaces: [], unions: [], objects }, + }); - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - - expect(warn).toHaveBeenCalledWith( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@relationshipProperties" types. Found: [String]' ); - expect(warn).toHaveBeenCalledOnce(); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "roles"]); }); - test("works for non-nullable lists", () => { + test("should not raise when a list of nullable elements is found on relationship properties", () => { + const relationshipProps = gql` + type ActedIn @relationshipProperties { + roles: [String!] + } + `; const doc = gql` + ${relationshipProps} type Movie @node { - id: [[ID!]!]! + title: String + } + + type Actor @node { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; + const objects = relationshipProps.definitions as ObjectTypeDefinitionNode[]; + const executeValidate = () => + validateDocument({ + document: doc, + features: {}, + additionalDefinitions: { enums: [], interfaces: [], unions: [], objects }, + }); - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); + const errors = getError(executeValidate); - expect(warn).toHaveBeenCalledWith( - "Encountered list field definition(s) with list elements. This is not supported by Neo4j, however, you can ignore this warning if the field is only used in the result of custom resolver/Cypher." + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); + + test("should raise when a list of list is found", () => { + const doc = gql` + type Movie @node { + id: [[ID]!] + } + `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + 'List of nullable elements are not supported in "@node" types. Found: [[ID]!]' ); - expect(warn).toHaveBeenCalledOnce(); + expect(errors[0]).toHaveProperty("path", ["Movie", "id"]); }); -}); -describe("single relationship deprecation warning", () => { - let warn: jest.SpyInstance; + test("should not raise when a list of non-nullable elements is found", () => { + const doc = gql` + type Movie @node { + id: [ID!] + } + `; + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); + const errors = getError(executeValidate); - afterEach(() => { - warn.mockReset(); + expect(errors).toBeInstanceOf(NoErrorThrownError); }); - test("deprecated warning triggers on single relationships", () => { + test("should not raise when a list of non-nullable elements is found on @cypher fields", () => { const doc = gql` type Movie @node { - id: ID - topActor: Actor @relationship(type: "TOP_ACTOR", direction: OUT) + id: [ID] @cypher(statement: "RETURN [1,2,3] as ids", columnName: "ids") } + `; - type Actor @node { - name: String + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + + const errors = getError(executeValidate); + + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); + + test("should not raise when a list of non-nullable elements is found on non @node types", () => { + const doc = gql` + type Movie { + id: [ID] } `; - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - expect(warn).toHaveBeenCalledWith( - "Using @relationship directive on a non-list element is deprecated and will be removed in next major version." - ); - expect(warn).toHaveBeenCalledOnce(); + const errors = getError(executeValidate); + + expect(errors).toBeInstanceOf(NoErrorThrownError); + }); +}); + +describe("single relationship deprecation warning", () => { + let warn: jest.SpyInstance; + + beforeEach(() => { + warn = jest.spyOn(console, "warn").mockImplementation(() => {}); + }); + + afterEach(() => { + warn.mockReset(); }); test("deprecated warning does not trigger on list relationships", () => { @@ -372,70 +438,6 @@ describe("warns if queryDirection deprecated values are used", () => { warn.mockReset(); }); - test("should warn if queryDirection with DEFAULT prefix is used", () => { - const doc = gql` - type Actor @node { - name: String - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - type Movie @node { - title: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: IN, queryDirection: DEFAULT_DIRECTED) - } - `; - - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - expect(warn).toHaveBeenCalledExactlyOnceWith( - `Found @relationship argument "queryDirection" used with DEFAULT_DIRECTED which is deprecated. \n These default values were used to set a default for the "directed" argument, which is also now deprecated.` - ); - }); - - test("should warn if queryDirection with DIRECTED_ONLY suffix is used", () => { - const doc = gql` - type Actor @node { - name: String - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - type Movie @node { - title: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: IN, queryDirection: DIRECTED_ONLY) - } - `; - - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - expect(warn).toHaveBeenCalledExactlyOnceWith( - `Found @relationship argument "queryDirection" used with DIRECTED_ONLY which is deprecated. Please use "DIRECTED" or "UNDIRECTED" instead.` - ); - }); - - test("should warn once if multiple deprecation values are used", () => { - const doc = gql` - type Actor @node { - name: String - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, queryDirection: DEFAULT_DIRECTED) - } - type Movie @node { - title: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: IN, queryDirection: DIRECTED_ONLY) - } - `; - - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - expect(warn).toHaveBeenCalledTimes(1); - }); - test("should not warn if no deprecation values are used", () => { const doc = gql` type Actor @node { @@ -602,14 +604,16 @@ describe("validation 2.0", () => { name: String } `; - // TODO: is "[FullTextInput]!" type exposed to the user? + // TODO: is "[FulltextInput]!" type exposed to the user? expect(() => validateDocument({ document: doc, features: {}, additionalDefinitions })).toThrow( - 'Directive "@fulltext" argument "indexes" of type "[FullTextInput]!" is required, but it was not provided.' + 'Directive "@fulltext" argument "indexes" of type "[FulltextInput]!" is required, but it was not provided.' ); }); test("@fulltext ok", () => { const doc = gql` - type User @fulltext(indexes: [{ fields: ["name"] }]) @node { + type User + @fulltext(indexes: [{ indexName: "UserIndex", queryName: "usersByName", fields: ["name"] }]) + @node { name: String } `; @@ -766,7 +770,7 @@ describe("validation 2.0", () => { test("@relationship properties required", () => { const doc = gql` type User @node { - name: Post @relationship + name: [Post!]! @relationship } type Post @node { title: String @@ -786,7 +790,7 @@ describe("validation 2.0", () => { test("@relationship type required", () => { const doc = gql` type User @node { - name: Post @relationship(direction: IN) + name: [Post!]! @relationship(direction: IN) } type Post @node { title: String @@ -806,7 +810,7 @@ describe("validation 2.0", () => { test("@relationship direction required", () => { const doc = gql` type User @node { - name: Post @relationship(type: "HAS_POST") + name: [Post!]! @relationship(type: "HAS_POST") } type Post @node { title: String @@ -826,7 +830,7 @@ describe("validation 2.0", () => { test("@relationship ok", () => { const doc = gql` type User @node { - name: Post @relationship(direction: IN, type: "HAS_POST") + name: [Post!]! @relationship(direction: IN, type: "HAS_POST") } type Post @node { title: String @@ -841,13 +845,41 @@ describe("validation 2.0", () => { }); expect(executeValidate).not.toThrow(); }); + + test("Error on 1-1 relationships", () => { + const doc = gql` + type Movie @node { + id: ID + actors: Actor @relationship(type: "ACTED_IN", direction: OUT) + } + + type Actor @node { + name: String + movie: Movie! @relationship(type: "ACTED_IN", direction: IN) + } + `; + + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + const errors = getError(executeValidate); + expect(errors).toHaveLength(2); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + `Using @relationship directive on a non-list property "actors" is not supported.` + ); + expect(errors[1]).toHaveProperty( + "message", + `Using @relationship directive on a non-list property "movie" is not supported.` + ); + }); }); }); describe("Directive Argument Type", () => { test("@fulltext.indexes property required", () => { const doc = gql` - type User @fulltext(indexes: [{ name: "something" }]) @node { + type User @fulltext(indexes: [{ indexName: "something", queryName: "something" }]) @node { name: String } `; @@ -868,7 +900,7 @@ describe("validation 2.0", () => { type User @node { name: String } - extend type User @fulltext(indexes: [{ name: "something" }]) + extend type User @fulltext(indexes: [{ indexName: "something", queryName: "something" }]) `; const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); @@ -885,7 +917,7 @@ describe("validation 2.0", () => { test("@relationship.direction property must be enum value", () => { const doc = gql` type User @node { - post: Post @relationship(direction: "EVERYWHERE", type: "HAS_NAME") + post: [Post!]! @relationship(direction: "EVERYWHERE", type: "HAS_NAME") } type Post @node { title: String @@ -914,7 +946,7 @@ describe("validation 2.0", () => { id: ID } extend type User { - post: Post @relationship(direction: "EVERYWHERE", type: "HAS_NAME") + post: [Post!]! @relationship(direction: "EVERYWHERE", type: "HAS_NAME") } type Post @node { title: String @@ -940,7 +972,7 @@ describe("validation 2.0", () => { test("@relationship.type property must be string", () => { const doc = gql` type User @node { - post: Post @relationship(type: 42, direction: IN) + post: [Post!]! @relationship(type: 42, direction: IN) } type Post @node { title: String @@ -971,7 +1003,7 @@ describe("validation 2.0", () => { `; const doc = gql` type User implements Person @node { - post: Post @relationship(type: 42, direction: IN) + post: [Post!]! @relationship(type: 42, direction: IN) } type Post @node { title: String @@ -1008,7 +1040,7 @@ describe("validation 2.0", () => { const doc = gql` type User implements Person @node { id: ID - post: Post @relationship(type: 42, direction: IN) + post: [Post!]! @relationship(type: 42, direction: IN) } type Post @node { title: String @@ -1169,7 +1201,10 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "@default.value is not a valid DateTime"); + expect(errors[0]).toHaveProperty( + "message", + "@default.value on DateTime fields must be of type DateTime" + ); expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@default", "value"]); }); @@ -1194,7 +1229,10 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "@default.value is not a valid DateTime"); + expect(errors[0]).toHaveProperty( + "message", + "@default.value on DateTime fields must be of type DateTime" + ); expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@default", "value"]); }); @@ -1369,7 +1407,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: "dummy") + statuses: [Status!] @default(value: "dummy") } `; @@ -1403,7 +1441,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: ["dummy"]) + statuses: [Status!] @default(value: ["dummy"]) } `; @@ -1437,7 +1475,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @default(value: [PENDING]) + statuses: [Status!] @default(value: [PENDING]) } `; const enums = enumTypes.definitions as EnumTypeDefinitionNode[]; @@ -1494,7 +1532,7 @@ describe("validation 2.0", () => { test("@default on int list must be list of int values", () => { const doc = gql` type User @node { - ages: [Int] @default(value: ["dummy"]) + ages: [Int!] @default(value: ["dummy"]) } `; @@ -1518,7 +1556,7 @@ describe("validation 2.0", () => { test("@default on int list must be list of int values correct", () => { const doc = gql` type User @node { - ages: [Int] @default(value: [12]) + ages: [Int!] @default(value: [12]) } `; @@ -1569,7 +1607,7 @@ describe("validation 2.0", () => { test("@default on float list must be list of float values", () => { const doc = gql` type User @node { - avgs: [Float] @default(value: [1]) + avgs: [Float!] @default(value: [1]) } `; @@ -1586,7 +1624,7 @@ describe("validation 2.0", () => { test("@default on float list must be list of float values correct", () => { const doc = gql` type User @node { - avgs: [Float] @default(value: [1.2]) + avgs: [Float!] @default(value: [1.2]) } `; @@ -1641,7 +1679,7 @@ describe("validation 2.0", () => { test("@default on boolean list must be list of boolean values", () => { const doc = gql` type User @node { - statuses: [Boolean] @default(value: [2]) + statuses: [Boolean!] @default(value: [2]) } `; @@ -1665,7 +1703,7 @@ describe("validation 2.0", () => { test("@default on boolean list must be list of boolean values correct", () => { const doc = gql` type User @node { - statuses: [Boolean] @default(value: [true]) + statuses: [Boolean!] @default(value: [true]) } `; @@ -1720,7 +1758,7 @@ describe("validation 2.0", () => { test("@default on string list must be list of string values", () => { const doc = gql` type User @node { - names: [String] @default(value: [2]) + names: [String!] @default(value: [2]) } `; @@ -1744,7 +1782,7 @@ describe("validation 2.0", () => { test("@default on string list must be list of string values correct", () => { const doc = gql` type User @node { - names: [String] @default(value: ["Bob"]) + names: [String!] @default(value: ["Bob"]) } `; @@ -1782,7 +1820,7 @@ describe("validation 2.0", () => { test("@default on ID list must be list of ID values", () => { const doc = gql` type User @node { - ids: [ID] @default(value: [2]) + ids: [ID!] @default(value: [2]) } `; @@ -1806,7 +1844,7 @@ describe("validation 2.0", () => { test("@default on ID list must be list of ID values correct", () => { const doc = gql` type User @node { - ids: [ID] @default(value: ["123-223"]) + ids: [ID!] @default(value: ["123-223"]) } `; @@ -1992,7 +2030,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: "dummy") + statuses: [Status!] @coalesce(value: "dummy") } `; @@ -2026,7 +2064,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: ["dummy"]) + statuses: [Status!] @coalesce(value: ["dummy"]) } `; @@ -2060,7 +2098,7 @@ describe("validation 2.0", () => { const doc = gql` ${enumTypes} type User @node { - statuses: [Status] @coalesce(value: [PENDING]) + statuses: [Status!] @coalesce(value: [PENDING]) } `; @@ -2118,7 +2156,7 @@ describe("validation 2.0", () => { test("@coalesce on int list must be list of int values", () => { const doc = gql` type User @node { - ages: [Int] @coalesce(value: ["dummy"]) + ages: [Int!] @coalesce(value: ["dummy"]) } `; @@ -2142,7 +2180,7 @@ describe("validation 2.0", () => { test("@coalesce on int list must be list of int values correct", () => { const doc = gql` type User @node { - ages: [Int] @coalesce(value: [12]) + ages: [Int!] @coalesce(value: [12]) } `; @@ -2193,7 +2231,7 @@ describe("validation 2.0", () => { test("@coalesce on float list must be list of float values", () => { const doc = gql` type User @node { - avgs: [Float] @coalesce(value: [1]) + avgs: [Float!] @coalesce(value: [1]) } `; @@ -2210,7 +2248,7 @@ describe("validation 2.0", () => { test("@coalesce on float list must be list of float values correct", () => { const doc = gql` type User @node { - avgs: [Float] @coalesce(value: [1.2]) + avgs: [Float!] @coalesce(value: [1.2]) } `; @@ -2268,7 +2306,7 @@ describe("validation 2.0", () => { test("@coalesce on boolean list must be list of boolean values", () => { const doc = gql` type User @node { - statuses: [Boolean] @coalesce(value: [2]) + statuses: [Boolean!] @coalesce(value: [2]) } `; @@ -2292,7 +2330,7 @@ describe("validation 2.0", () => { test("@coalesce on boolean list must be list of boolean values correct", () => { const doc = gql` type User @node { - statuses: [Boolean] @coalesce(value: [true]) + statuses: [Boolean!] @coalesce(value: [true]) } `; @@ -2347,7 +2385,7 @@ describe("validation 2.0", () => { test("@coalesce on string list must be list of string values", () => { const doc = gql` type User @node { - names: [String] @coalesce(value: [2]) + names: [String!] @coalesce(value: [2]) } `; @@ -2371,7 +2409,7 @@ describe("validation 2.0", () => { test("@coalesce on string list must be list of string values correct", () => { const doc = gql` type User @node { - names: [String] @coalesce(value: ["Bob"]) + names: [String!] @coalesce(value: ["Bob"]) } `; @@ -2409,7 +2447,7 @@ describe("validation 2.0", () => { test("@coalesce on ID list must be list of ID values", () => { const doc = gql` type User @node { - ids: [ID] @coalesce(value: [2]) + ids: [ID!] @coalesce(value: [2]) } `; @@ -2433,7 +2471,7 @@ describe("validation 2.0", () => { test("@coalesce on ID list must be list of ID values correct", () => { const doc = gql` type User @node { - ids: [ID] @coalesce(value: ["123-223"]) + ids: [ID!] @coalesce(value: ["123-223"]) } `; @@ -2482,7 +2520,7 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty("message", "@coalesce is not supported by Spatial types."); - expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@coalesce", "value"]); + expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@coalesce"]); }); test("@coalesce not supported on Temporal types", () => { @@ -2503,7 +2541,7 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty("message", "@coalesce is not supported by Temporal types."); - expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@coalesce", "value"]); + expect(errors[0]).toHaveProperty("path", ["User", "updatedAt", "@coalesce"]); }); test("@coalesce only supported on scalar types", () => { @@ -2658,7 +2696,13 @@ describe("validation 2.0", () => { test("@fulltext duplicate index names", () => { const doc = gql` type User - @fulltext(indexes: [{ indexName: "a", fields: ["name"] }, { indexName: "a", fields: ["id"] }]) { + @node + @fulltext( + indexes: [ + { indexName: "a", queryName: "a", fields: ["name"] } + { indexName: "a", queryName: "b", fields: ["id"] } + ] + ) { name: String id: ID } @@ -2669,7 +2713,10 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "@fulltext.indexes invalid value for: a. Duplicate name."); + expect(errors[0]).toHaveProperty( + "message", + "@fulltext.indexes invalid value for: a. Duplicate index name." + ); expect(errors[0]).toHaveProperty("path", ["User", "@fulltext", "indexes"]); }); @@ -2680,7 +2727,39 @@ describe("validation 2.0", () => { id: ID } extend type User - @fulltext(indexes: [{ indexName: "a", fields: ["name"] }, { indexName: "a", fields: ["id"] }]) + @fulltext( + indexes: [ + { indexName: "a", queryName: "a", fields: ["name"] } + { indexName: "a", queryName: "b", fields: ["id"] } + ] + ) + `; + + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + const errors = getError(executeValidate); + + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + "@fulltext.indexes invalid value for: a. Duplicate index name." + ); + expect(errors[0]).toHaveProperty("path", ["User", "@fulltext", "indexes"]); + }); + + test("@fulltext duplicate query names", () => { + const doc = gql` + type User + @node + @fulltext( + indexes: [ + { indexName: "a", queryName: "a", fields: ["name"] } + { indexName: "b", queryName: "a", fields: ["id"] } + ] + ) { + name: String + id: ID + } `; const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); @@ -2688,13 +2767,43 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "@fulltext.indexes invalid value for: a. Duplicate name."); + expect(errors[0]).toHaveProperty( + "message", + "@fulltext.indexes invalid value for: a. Duplicate query name." + ); + expect(errors[0]).toHaveProperty("path", ["User", "@fulltext", "indexes"]); + }); + + test("@fulltext duplicate query names extension", () => { + const doc = gql` + type User @node { + name: String + id: ID + } + extend type User + @fulltext( + indexes: [ + { indexName: "a", queryName: "a", fields: ["name"] } + { indexName: "b", queryName: "a", fields: ["id"] } + ] + ) + `; + + const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); + const errors = getError(executeValidate); + + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( + "message", + "@fulltext.indexes invalid value for: a. Duplicate query name." + ); expect(errors[0]).toHaveProperty("path", ["User", "@fulltext", "indexes"]); }); test("@fulltext index on type not String or ID", () => { const doc = gql` - type User @fulltext(indexes: [{ indexName: "a", fields: ["age"] }]) @node { + type User @fulltext(indexes: [{ indexName: "a", queryName: "a", fields: ["age"] }]) @node { age: Int } `; @@ -2714,7 +2823,13 @@ describe("validation 2.0", () => { test("@fulltext correct usage", () => { const doc = gql` type User - @fulltext(indexes: [{ indexName: "a", fields: ["name"] }, { indexName: "b", fields: ["id"] }]) { + @node + @fulltext( + indexes: [ + { indexName: "a", queryName: "a", fields: ["name"] } + { indexName: "b", queryName: "b", fields: ["id"] } + ] + ) { id: ID name: String } @@ -2886,12 +3001,10 @@ describe("validation 2.0", () => { }); test("@relationship relationshipProperties type not annotated with @relationshipProperties", () => { - const relationshipProperties = gql` + const doc = gql` type Poster @node { createdAt: String } - `; - const doc = gql` type User @node { name: String posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT, properties: "Poster") @@ -2904,11 +3017,10 @@ describe("validation 2.0", () => { const enums = [] as EnumTypeDefinitionNode[]; const interfaces = [] as InterfaceTypeDefinitionNode[]; const unions = [] as UnionTypeDefinitionNode[]; - const objects = relationshipProperties.definitions as ObjectTypeDefinitionNode[]; const executeValidate = () => validateDocument({ document: doc, - additionalDefinitions: { enums, interfaces, unions, objects }, + additionalDefinitions: { enums, interfaces, unions, objects: [] }, features: {}, }); const errors = getError(executeValidate); @@ -2922,18 +3034,16 @@ describe("validation 2.0", () => { }); test("@relationship correct usage", () => { - const relationshipProps = gql` + const doc = gql` type Poster @relationshipProperties { createdAt: String } - `; - const doc = gql` type User @node { name: String posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT, properties: "Poster") archived: [Post!]! @relationship(type: "HAS_ARCHIVED_POST", direction: OUT, properties: "Poster") - favorite: Post @relationship(type: "HAS_FAVORITE", direction: OUT) + favorite: [Post!]! @relationship(type: "HAS_FAVORITE", direction: OUT) } type Post @node { title: String @@ -2943,11 +3053,10 @@ describe("validation 2.0", () => { const enums = [] as EnumTypeDefinitionNode[]; const interfaces = [] as InterfaceTypeDefinitionNode[]; const unions = [] as UnionTypeDefinitionNode[]; - const objects = relationshipProps.definitions as ObjectTypeDefinitionNode[]; const executeValidate = () => validateDocument({ document: doc, - additionalDefinitions: { enums, interfaces, unions, objects }, + additionalDefinitions: { enums, interfaces, unions, objects: [] }, features: {}, }); expect(executeValidate).not.toThrow(); @@ -3110,38 +3219,6 @@ describe("validation 2.0", () => { }); }); - describe("@unique", () => { - test("@unique valid", () => { - const doc = gql` - type User @node { - name: String @unique - } - `; - - const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - expect(executeValidate).not.toThrow(); - }); - - test("@unique cannot be used on fields of Interface types", () => { - const doc = gql` - interface IUser { - name: String @unique - } - `; - - const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @unique is not supported on fields of the IUser type." - ); - expect(errors[0]).toHaveProperty("path", ["IUser", "name", "@unique"]); - }); - }); - test("should throw cannot auto-generate a non ID field", () => { const doc = gql` type Movie @node { @@ -3161,7 +3238,7 @@ describe("validation 2.0", () => { test("should throw cannot auto-generate an array", () => { const doc = gql` type Movie @node { - name: [ID] @id + name: [ID!] @id } `; @@ -3189,7 +3266,7 @@ describe("validation 2.0", () => { test("@timestamp cannot autogenerate array", () => { const doc = gql` type User @node { - lastSeenAt: [DateTime] @timestamp + lastSeenAt: [DateTime!] @timestamp } `; @@ -3205,7 +3282,7 @@ describe("validation 2.0", () => { test("should throw cannot timestamp on array of DateTime", () => { const doc = gql` type Movie @node { - name: [DateTime] @timestamp(operations: [CREATE]) + name: [DateTime!] @timestamp(operations: [CREATE]) } `; @@ -3253,7 +3330,7 @@ describe("validation 2.0", () => { test("@id autogenerate cannot autogenerate array", () => { const doc = gql` type User @node { - uid: [ID] @id + uid: [ID!] @id } `; @@ -3324,7 +3401,7 @@ describe("validation 2.0", () => { test("@query and @mutation", () => { const doc = gql` - type User @query(read: false) { + type User @node @query(read: false) { id: ID name: String } @@ -3336,36 +3413,6 @@ describe("validation 2.0", () => { }); }); describe("invalid", () => { - test("@unique can't be used with @relationship", () => { - const doc = gql` - type Movie @node { - id: ID - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) @unique - } - - type Actor @node { - name: String - } - `; - - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @relationship cannot be used in combination with @unique" - ); - expect(errors[0]).toHaveProperty("path", ["Movie", "actors"]); - }); - test("@authentication can't be used with @relationship", () => { const doc = gql` type Movie @node { @@ -3610,7 +3657,7 @@ describe("validation 2.0", () => { test("@query both on extension and object", () => { const doc = gql` - type User @query(read: false) { + type User @node @query(read: false) { id: ID name: String } @@ -3630,7 +3677,7 @@ describe("validation 2.0", () => { test("@query both on schema and object", () => { const doc = gql` - type User @query(read: false) { + type User @node @query(read: false) { id: ID name: String } @@ -3673,10 +3720,7 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @relationship is not supported on fields of the Query type." - ); + expect(errors[0]).toHaveProperty("message", 'Directive "relationship" requires in a type with "@node"'); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@relationship"]); }); @@ -3706,10 +3750,7 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @relationship is not supported on fields of the Query type." - ); + expect(errors[0]).toHaveProperty("message", 'Directive "relationship" requires in a type with "@node"'); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@relationship"]); }); @@ -3737,7 +3778,7 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @authorization is not supported on fields of the Query type." + "Directive @authorization is not supported on fields of the Query type. Did you mean to use @authentication?" ); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@authorization"]); }); @@ -3769,7 +3810,7 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @authorization is not supported on fields of the Query type." + "Directive @authorization is not supported on fields of the Query type. Did you mean to use @authentication?" ); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@authorization"]); }); @@ -3805,7 +3846,7 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @authorization is not supported on fields of the Query type. Did you mean to use @authentication?" + "Directive @authorization is not supported on fields of the Query type. Did you mean to use @authentication?" ); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@authorization"]); }); @@ -3836,19 +3877,13 @@ describe("validation 2.0", () => { const errors = getError(executeValidate); - expect(errors).toHaveLength(2); + expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "@populatedBy can only be used on fields of type Int, Float, String, Boolean, ID, BigInt, DateTime, Date, Time, LocalDateTime, LocalTime or Duration." + 'Directive "populatedBy" requires in a type with "@node" or within the "@relationshipProperties" directive' ); expect(errors[0]).toHaveProperty("path", ["Query", "someActors", "@populatedBy"]); - expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[1]).toHaveProperty( - "message", - "Invalid directive usage: Directive @populatedBy is not supported on fields of the Query type." - ); - expect(errors[1]).toHaveProperty("path", ["Query", "someActors", "@populatedBy"]); }); test("@authentication ok to be used on the field of a root type", () => { @@ -3957,7 +3992,7 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @cypher is not supported on fields of the Subscription type." + 'Directive "cypher" requires in a type with "@node" or on root types: Query, and Mutation' ); expect(errors[0]).toHaveProperty("path", ["Subscription", "someActors", "@cypher"]); }); @@ -3994,7 +4029,7 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @cypher is not supported on fields of the Person type." + 'Directive "cypher" requires in a type with "@node" or on root types: Query, and Mutation' ); expect(errors[0]).toHaveProperty("path", ["Person", "name", "@cypher"]); }); @@ -4029,29 +4064,6 @@ describe("validation 2.0", () => { expect(errors[0]).toHaveProperty("path", ["Person", "actor", "@relationship"]); }); - test("@private ok to be used on the field of an interface type", () => { - const doc = gql` - interface Person { - name: String @private - id: ID - } - - type Actor implements Person @node { - name: String - id: ID - } - `; - - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions, - features: {}, - }); - - expect(executeValidate).not.toThrow(); - }); - test("@settable ok to be used on the field of an interface type", () => { const doc = gql` interface Person { @@ -4476,9 +4488,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid directive usage: Directive @jwtClaim cannot be used in combination with @cypher" + 'Directive "cypher" requires in a type with "@node" or on root types: Query, and Mutation' ); - expect(errors[0]).toHaveProperty("path", ["JWTPayload", "id"]); + expect(errors[0]).toHaveProperty("path", ["JWTPayload", "id", "@cypher"]); }); test("@jwtClaim incorrect location outside @jwt", () => { @@ -4772,10 +4784,7 @@ describe("validation 2.0", () => { expect(errors).toHaveLength(2); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty( - "message", - "Invalid directive usage: Directive @relayId is not supported on fields of the MovieInterface type." - ); + expect(errors[0]).toHaveProperty("message", 'Directive "relayId" requires in a type with "@node"'); expect(errors[0]).toHaveProperty("path", ["MovieInterface", "imdbid", "@relayId"]); expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[1]).toHaveProperty( @@ -4913,137 +4922,6 @@ describe("validation 2.0", () => { }); describe("Objects and Interfaces must have one or more fields", () => { - describe("@private", () => { - test("should throw error if @private would leave no fields in interface", () => { - const interfaceTypes = gql` - interface UserInterface { - private: String @private - } - `; - const doc = gql` - ${interfaceTypes} - type User implements UserInterface @node { - id: ID - password: String @private - private: String - } - `; - - const enums = [] as EnumTypeDefinitionNode[]; - const interfaces = interfaceTypes.definitions as InterfaceTypeDefinitionNode[]; - const unions = [] as UnionTypeDefinitionNode[]; - const objects = [] as ObjectTypeDefinitionNode[]; - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions: { enums, interfaces, unions, objects }, - features: {}, - }); - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "Objects and Interfaces must have one or more fields."); - }); - - test("should throw error if @private would leave no fields in object", () => { - const interfaceTypes = gql` - interface UserInterface { - private: String @private - } - `; - const doc = gql` - ${interfaceTypes} - type User implements UserInterface @node { - id: ID - password: String @private - private: String - } - `; - - const enums = [] as EnumTypeDefinitionNode[]; - const interfaces = interfaceTypes.definitions as InterfaceTypeDefinitionNode[]; - const unions = [] as UnionTypeDefinitionNode[]; - const objects = [] as ObjectTypeDefinitionNode[]; - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions: { enums, interfaces, unions, objects }, - features: {}, - }); - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "Objects and Interfaces must have one or more fields."); - }); - - test("should throw error if @private would leave no fields in object extension", () => { - const interfaceTypes = gql` - interface UserInterface { - private: String - } - `; - const doc = gql` - ${interfaceTypes} - type User implements UserInterface @node { - password: String @private - } - extend type User { - private: String @private - } - `; - - const enums = [] as EnumTypeDefinitionNode[]; - const interfaces = interfaceTypes.definitions as InterfaceTypeDefinitionNode[]; - const unions = [] as UnionTypeDefinitionNode[]; - const objects = [] as ObjectTypeDefinitionNode[]; - const executeValidate = () => - validateDocument({ - document: doc, - additionalDefinitions: { enums, interfaces, unions, objects }, - features: {}, - }); - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "Objects and Interfaces must have one or more fields."); - }); - - test("Interfaces must have one or more fields", () => { - const doc = gql` - interface Production - type Movie implements Production @node { - id: ID! - title: String - } - `; - - const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - const errors = getError(executeValidate); - - expect(errors).toHaveLength(1); - expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[0]).toHaveProperty("message", "Objects and Interfaces must have one or more fields."); - }); - - test("valid", () => { - const doc = gql` - type Movie implements Production @node { - id: ID! - title: String - episodes: Int - } - interface Production { - episodes: Int - } - `; - - const executeValidate = () => validateDocument({ document: doc, features: {}, additionalDefinitions }); - expect(executeValidate).not.toThrow(); - }); - }); describe("@authorization", () => { test("should throw error if there are no arguments", () => { const doc = gql` @@ -5081,7 +4959,7 @@ describe("validation 2.0", () => { const doc = gql` type Movie @node { id: ID! - title: String @authorization(test: "test") + title: String @authorization } `; @@ -5091,10 +4969,9 @@ describe("validation 2.0", () => { const error = `@authorization requires at least one of ${AuthorizationAnnotationArguments.join( ", " )} arguments`; - expect(errors).toHaveLength(2); - expect(errors[0]).toHaveProperty("message", `Unknown argument "test" on directive "@authorization".`); - expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[1]).toHaveProperty("message", error); + expect(errors).toHaveLength(1); + expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty("message", error); }); }); describe("@subscriptionsAuthorization", () => { @@ -5164,7 +5041,7 @@ describe("validation 2.0", () => { const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5189,9 +5066,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @authorization directive on relationship properties." + 'Directive "@authorization" requires in a type with "@node"' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime", "@authorization"]); }); test("should throw error if @authorization is used on relationship property extension", () => { @@ -5206,7 +5083,7 @@ describe("validation 2.0", () => { const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5231,9 +5108,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @authorization directive on relationship properties." + 'Directive "@authorization" requires in a type with "@node"' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime", "@authorization"]); }); test("should throw error if @authentication is used on relationship property", () => { const relationshipProperties = gql` @@ -5244,7 +5121,7 @@ describe("validation 2.0", () => { const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5269,9 +5146,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @authentication directive on relationship properties." + 'Directive "authentication" requires in a type with "@node" or in root types: Query, and Mutation' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime", "@authentication"]); }); test("should throw error if @subscriptionsAuthorization is used on relationship property", () => { const relationshipProperties = gql` @@ -5282,7 +5159,7 @@ describe("validation 2.0", () => { const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5307,21 +5184,21 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @subscriptionsAuthorization directive on relationship properties." + 'Directive "@subscriptionsAuthorization" requires in a type with "@node"' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "screenTime", "@subscriptionsAuthorization"]); }); test("should throw error if @relationship is used on relationship property", () => { const relationshipProperties = gql` type ActedIn @relationshipProperties { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5346,22 +5223,22 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @relationship directive on relationship properties." + 'Directive "relationship" requires in a type with "@node"' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "actors"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "actors", "@relationship"]); }); test("should throw error if @cypher is used on relationship property", () => { const relationshipProperties = gql` type ActedIn @relationshipProperties { id: ID @cypher(statement: "RETURN id(this) as id", columnName: "id") - roles: [String] + roles: [String!] } `; const doc = gql` ${relationshipProperties} type Movie @node { - actors: Actor! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Actor @node { @@ -5386,9 +5263,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @cypher directive on relationship properties." + 'Directive "cypher" requires in a type with "@node" or on root types: Query, and Mutation' ); - expect(errors[0]).toHaveProperty("path", ["ActedIn", "id"]); + expect(errors[0]).toHaveProperty("path", ["ActedIn", "id", "@cypher"]); }); test("@relationshipProperties reserved field name", () => { @@ -5470,9 +5347,9 @@ describe("validation 2.0", () => { expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); expect(errors[0]).toHaveProperty( "message", - "Invalid @relationshipProperties field: Cannot use the @cypher directive on relationship properties." + 'Directive "cypher" requires in a type with "@node" or on root types: Query, and Mutation' ); - expect(errors[0]).toHaveProperty("path", ["HasPost", "review"]); + expect(errors[0]).toHaveProperty("path", ["HasPost", "review", "@cypher"]); }); }); @@ -5601,7 +5478,6 @@ describe("validation 2.0", () => { const doc = gql` type User @node { name: String - posts: Int! @relationship(type: "HAS_POST", direction: OUT) allPosts: [Int!] @relationship(type: "HAS_POST", direction: OUT) } `; @@ -5614,19 +5490,14 @@ describe("validation 2.0", () => { }); const errors = getError(executeValidate); - expect(errors).toHaveLength(2); + expect(errors).toHaveLength(1); expect(errors[0]).not.toBeInstanceOf(NoErrorThrownError); - expect(errors[1]).not.toBeInstanceOf(NoErrorThrownError); + expect(errors[0]).toHaveProperty( "message", "Invalid field type: Scalar types cannot be relationship targets. Please use an Object type instead." ); - expect(errors[1]).toHaveProperty( - "message", - "Invalid field type: Scalar types cannot be relationship targets. Please use an Object type instead." - ); - expect(errors[0]).toHaveProperty("path", ["User", "posts"]); - expect(errors[1]).toHaveProperty("path", ["User", "allPosts"]); + expect(errors[0]).toHaveProperty("path", ["User", "allPosts"]); }); }); @@ -5634,7 +5505,7 @@ describe("validation 2.0", () => { test("simple list", () => { const doc = gql` type Post @node { - titles: [String] + titles: [String!] } `; @@ -6080,7 +5951,7 @@ describe("validation 2.0", () => { `; expect(() => validateDocument({ document: doc, features: undefined, additionalDefinitions })).toThrow( - 'Directive "@fulltext" argument "indexes" of type "[FullTextInput]!" is required, but it was not provided.' + 'Directive "@fulltext" argument "indexes" of type "[FulltextInput]!" is required, but it was not provided.' ); }); @@ -6150,7 +6021,7 @@ describe("validation 2.0", () => { } type Post @node { - id: ID! @id @unique + id: ID! @id title: String! datetime: DateTime @timestamp(operations: [CREATE]) } @@ -6234,7 +6105,7 @@ describe("validation 2.0", () => { eligibleForBonus: Boolean bonusPercentage: Float salaryReviewDate: DateTime - pays_salary: EmploymentRecord! @relationship(type: "PAYS_SALARY", direction: IN) + pays_salary: [EmploymentRecord!]! @relationship(type: "PAYS_SALARY", direction: IN) } type EmploymentRecord @node { @@ -6460,10 +6331,10 @@ describe("validation 2.0", () => { test("should not throw error on validation of schema", () => { const doc = gql` type Order @node { - orderID: ID! @id @unique + orderID: ID! @id placedAt: DateTime @timestamp - shipTo: Address! @relationship(type: "SHIPS_TO", direction: OUT) - customer: Customer! @relationship(type: "PLACED", direction: IN) + shipTo: [Address!]! @relationship(type: "SHIPS_TO", direction: OUT) + customer: [Customer!]! @relationship(type: "PLACED", direction: IN) books: [Book!]! @relationship(type: "CONTAINS", direction: OUT) } @@ -6495,7 +6366,7 @@ describe("validation 2.0", () => { type Address @node { address: String location: Point - order: Order @relationship(type: "SHIPS_TO", direction: IN) + order: [Order!]! @relationship(type: "SHIPS_TO", direction: IN) } extend type Address { @@ -6544,8 +6415,8 @@ describe("validation 2.0", () => { rating: Int text: String createdAt: DateTime @timestamp - book: Book! @relationship(type: "REVIEWS", direction: OUT) - author: Customer! @relationship(type: "WROTE", direction: IN) + book: [Book!]! @relationship(type: "REVIEWS", direction: OUT) + author: [Customer!]! @relationship(type: "WROTE", direction: IN) } type Author @node { diff --git a/packages/graphql/src/schema/validation/validate-document.ts b/packages/graphql/src/schema/validation/validate-document.ts index 558b5a9f09..67e757da1f 100644 --- a/packages/graphql/src/schema/validation/validate-document.ts +++ b/packages/graphql/src/schema/validation/validate-document.ts @@ -35,7 +35,10 @@ import { GraphQLSchema, Kind, extendSchema, specifiedDirectives, validateSchema import { specifiedSDLRules } from "graphql/validation/specifiedRules"; import pluralize from "pluralize"; import * as directives from "../../graphql/directives"; +import { authenticationDirectiveScaffold } from "../../graphql/directives/type-dependant-directives/authentication"; +import { authorizationDirectiveScaffold } from "../../graphql/directives/type-dependant-directives/authorization"; import { typeDependantDirectivesScaffolds } from "../../graphql/directives/type-dependant-directives/scaffolds"; +import { subscriptionsAuthorizationDirectiveScaffold } from "../../graphql/directives/type-dependant-directives/subscriptions-authorization"; import { SortDirection } from "../../graphql/enums/SortDirection"; import { CartesianPointDistance } from "../../graphql/input-objects/CartesianPointDistance"; import { CartesianPointInput } from "../../graphql/input-objects/CartesianPointInput"; @@ -46,9 +49,20 @@ import { Point } from "../../graphql/objects/Point"; import * as scalars from "../../graphql/scalars"; import type { Neo4jFeaturesSettings } from "../../types"; import { isRootType } from "../../utils/is-root-type"; -import { DirectiveArgumentOfCorrectType } from "./custom-rules/directive-argument-of-correct-type"; -import { directiveIsValid } from "./custom-rules/directives/valid-directive"; -import { ValidDirectiveAtFieldLocation } from "./custom-rules/directives/valid-directive-field-location"; +import { validateAuthenticationDirective } from "./custom-rules/directives/authentication"; +import { validateAuthorizationDirective } from "./custom-rules/directives/authorization"; +import { validateCoalesceDirective } from "./custom-rules/directives/coalesce"; +import { validateCypherDirective } from "./custom-rules/directives/cypher"; +import { validateDefaultDirective } from "./custom-rules/directives/default"; +import { validateFulltextDirective } from "./custom-rules/directives/fulltext"; +import { validateIdDirective } from "./custom-rules/directives/id"; +import { validateLimitDirective } from "./custom-rules/directives/limit"; +import { validatePopulatedByDirective } from "./custom-rules/directives/populated-by"; +import { validateRelationshipDirective } from "./custom-rules/directives/relationship"; +import { validateRelayIdDirective } from "./custom-rules/directives/relay-id"; +import { validateSubscriptionAuthorizationDirective } from "./custom-rules/directives/subscriptionAuthorization"; +import { validateTimestampDirective } from "./custom-rules/directives/timestamp"; +import { ErrorIfSingleRelationships } from "./custom-rules/error-single-relationships"; import { ValidJwtDirectives } from "./custom-rules/features/valid-jwt-directives"; import { ValidRelationshipDeclaration } from "./custom-rules/features/valid-relationship-declaration"; import { ValidRelationshipProperties } from "./custom-rules/features/valid-relationship-properties"; @@ -60,17 +74,13 @@ import { SchemaOrTypeDirectives, } from "./custom-rules/valid-types/valid-directive-combination"; import { ValidFieldTypes } from "./custom-rules/valid-types/valid-field-types"; +import { ValidListInNodeType } from "./custom-rules/valid-types/valid-list-in-node-type"; import { ValidObjectType } from "./custom-rules/valid-types/valid-object-type"; +import { ValidateNeo4jDirectiveArgumentsValue } from "./custom-rules/validate-neo4j-directive-arguments-value"; import { WarnIfAuthorizationFeatureDisabled } from "./custom-rules/warnings/authorization-feature-disabled"; -import { WarnPrivateDeprecation } from "./custom-rules/warnings/deprecated-private"; -import { WarnUniqueDeprecation } from "./custom-rules/warnings/deprecated-unique"; import { WarnIfAMaxLimitCanBeBypassedThroughInterface } from "./custom-rules/warnings/limit-max-can-be-bypassed"; -import { WarnIfListOfListsFieldDefinition } from "./custom-rules/warnings/list-of-lists"; import { WarnObjectFieldsWithoutResolver } from "./custom-rules/warnings/object-fields-without-resolver"; -import { WarnIfQueryDirectionIsUsedWithDeprecatedValues } from "./custom-rules/warnings/query-direction-deprecated-values"; -import { WarnIfSingleRelationships } from "./custom-rules/warnings/single-relationship"; import { WarnIfSubscriptionsAuthorizationMissing } from "./custom-rules/warnings/subscriptions-authorization-missing"; -import { WarnIfTypeIsNotMarkedAsNode } from "./custom-rules/warnings/warn-if-type-is-not-marked-as-node"; import { validateSchemaCustomizations } from "./validate-schema-customizations"; import { validateSDL } from "./validate-sdl"; @@ -142,7 +152,11 @@ function filterDocument(document: DocumentNode, filterDirectives: boolean = fals // currentDirectiveDirective is of type ConstDirectiveNode, has to be any to support GraphQL 15 const filterDirectiveNodes = (directives: readonly any[] | undefined): any[] | undefined => { return directives?.filter((directive) => { - return !["authentication", "authorization", "subscriptionsAuthorization"].includes(directive.name.value); + return ![ + authenticationDirectiveScaffold.name, + authorizationDirectiveScaffold.name, + subscriptionsAuthorizationDirectiveScaffold.name, + ].includes(directive.name.value); }); }; @@ -195,18 +209,11 @@ function filterDocument(document: DocumentNode, filterDirectives: boolean = fals function runValidationRulesOnFilteredDocument({ schema, document, - extra, userCustomResolvers, features, }: { schema: GraphQLSchema; document: DocumentNode; - extra: { - enums?: EnumTypeDefinitionNode[]; - interfaces?: InterfaceTypeDefinitionNode[]; - unions?: UnionTypeDefinitionNode[]; - objects?: ObjectTypeDefinitionNode[]; - }; userCustomResolvers?: IResolvers | Array; features: Neo4jFeaturesSettings | undefined; }) { @@ -214,8 +221,6 @@ function runValidationRulesOnFilteredDocument({ document, [ ...specifiedSDLRules, - directiveIsValid(extra, features?.populatedBy?.callbacks), - ValidDirectiveAtFieldLocation, DirectiveCombinationValid, SchemaOrTypeDirectives, ValidJwtDirectives, @@ -226,21 +231,32 @@ function runValidationRulesOnFilteredDocument({ ReservedTypeNames, ValidObjectType, ValidDirectiveInheritance, - DirectiveArgumentOfCorrectType(false), + ValidateNeo4jDirectiveArgumentsValue, WarnIfAuthorizationFeatureDisabled(features?.authorization), - WarnIfListOfListsFieldDefinition, - WarnIfSingleRelationships, + ErrorIfSingleRelationships, WarnIfAMaxLimitCanBeBypassedThroughInterface(), WarnObjectFieldsWithoutResolver({ customResolvers: asArray(userCustomResolvers ?? []), }), + ValidListInNodeType, WarnIfSubscriptionsAuthorizationMissing(Boolean(features?.subscriptions)), - WarnIfTypeIsNotMarkedAsNode(), - WarnIfQueryDirectionIsUsedWithDeprecatedValues, - WarnUniqueDeprecation(), - WarnPrivateDeprecation(), + // directives validations + validateAuthorizationDirective, + validateAuthenticationDirective, + validateCoalesceDirective, + validateCypherDirective, + validateDefaultDirective, + validateFulltextDirective, + validateIdDirective, + validateLimitDirective, + validatePopulatedByDirective, + validateRelationshipDirective, + validateRelayIdDirective, + validateTimestampDirective, + validateSubscriptionAuthorizationDirective, ], - schema + schema, + features?.populatedBy?.callbacks ); const filteredErrors = errors.filter((e) => e.message !== "Query root type must be provided."); if (filteredErrors.length) { @@ -267,7 +283,7 @@ function validateDocument({ userCustomResolvers?: IResolvers | Array; }): void { const filteredDocument = filterDocument(document); - const { additionalDirectives, additionalTypes, ...extra } = additionalDefinitions; + const { additionalDirectives, additionalTypes } = additionalDefinitions; const schemaToExtend = new GraphQLSchema({ directives: [ ...Object.values(directives), @@ -291,7 +307,6 @@ function validateDocument({ runValidationRulesOnFilteredDocument({ schema: schemaToExtend, document: filteredDocument, - extra, userCustomResolvers, features, }); diff --git a/packages/graphql/src/schema/validation/validate-schema-customizations.ts b/packages/graphql/src/schema/validation/validate-schema-customizations.ts index aa0b3228f4..485b23a51d 100644 --- a/packages/graphql/src/schema/validation/validate-schema-customizations.ts +++ b/packages/graphql/src/schema/validation/validate-schema-customizations.ts @@ -18,13 +18,13 @@ */ import type { DocumentNode, GraphQLSchema } from "graphql"; -import { getDefinitionNodes } from "../get-definition-nodes"; +import { getDefinitionCollection } from "../../schema-model/parser/definition-collection"; import { validateCustomResolverRequires } from "./validate-custom-resolver-requires"; export function validateSchemaCustomizations({ document, schema }: { document: DocumentNode; schema: GraphQLSchema }) { - const definitionNodes = getDefinitionNodes(document); + const definitionCollection = getDefinitionCollection(document); - for (const objectType of definitionNodes.objectTypes) { + for (const objectType of definitionCollection.objectTypes.values()) { validateCustomResolverRequires(objectType, schema); } } diff --git a/packages/graphql/src/schema/validation/validate-schema-model.ts b/packages/graphql/src/schema/validation/validate-schema-model.ts new file mode 100644 index 0000000000..bb65a0703b --- /dev/null +++ b/packages/graphql/src/schema/validation/validate-schema-model.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { GraphQLError } from "graphql"; +import type { Neo4jGraphQLSchemaModel } from "../../schema-model/Neo4jGraphQLSchemaModel"; +import { pluralConflict } from "./schema-model-rules/plural-conflict"; + +export type SchemaModelValidationRule = (model: Neo4jGraphQLSchemaModel) => GraphQLError[]; + +export function validateSchemaModel(schemaModel: Neo4jGraphQLSchemaModel): void { + const rules: SchemaModelValidationRule[] = [pluralConflict]; + + const errors = rules.flatMap((rule) => { + return rule(schemaModel); + }); + + if (errors.length > 0) { + throw errors; + } +} diff --git a/packages/graphql/src/schema/validation/validate-sdl.ts b/packages/graphql/src/schema/validation/validate-sdl.ts index 0794d55ea6..d42bc38157 100644 --- a/packages/graphql/src/schema/validation/validate-sdl.ts +++ b/packages/graphql/src/schema/validation/validate-sdl.ts @@ -18,22 +18,31 @@ */ import type { Maybe } from "@graphql-tools/utils/typings/types"; -import type { DocumentNode, GraphQLSchema, GraphQLError } from "graphql"; +import type { ASTVisitor, DocumentNode, GraphQLError, GraphQLSchema } from "graphql"; import { visit, visitInParallel } from "graphql"; -import type { SDLValidationRule } from "graphql/validation/ValidationContext"; -import { SDLValidationContext } from "graphql/validation/ValidationContext"; +import type { SDLValidationContext } from "graphql/validation/ValidationContext"; +import type { Neo4jGraphQLCallbacks } from "../../types"; +import { Neo4jValidationContext } from "./Neo4jValidationContext"; import { mapError } from "./utils/map-error"; +type Neo4jValidationRule = (context: T) => ASTVisitor; + export function validateSDL( documentAST: DocumentNode, - rules: ReadonlyArray, - schemaToExtend?: Maybe + rules: ReadonlyArray, + schemaToExtend?: Maybe, + callbacks?: Neo4jGraphQLCallbacks ): ReadonlyArray { const errors: Array = []; - const context = new SDLValidationContext(documentAST, schemaToExtend, (error) => { - const mappedError = mapError(error); - errors.push(mappedError); - }); + const context = new Neo4jValidationContext( + documentAST, + schemaToExtend, + (error) => { + const mappedError = mapError(error); + errors.push(mappedError); + }, + callbacks + ); const visitors = rules.map((rule) => rule(context)); visit(documentAST, visitInParallel(visitors)); return errors; diff --git a/packages/graphql/src/translate/authorization/utils/filter-by-values.ts b/packages/graphql/src/translate/authorization/utils/filter-by-values.ts index 1b64d0a339..fec3bdeb2a 100644 --- a/packages/graphql/src/translate/authorization/utils/filter-by-values.ts +++ b/packages/graphql/src/translate/authorization/utils/filter-by-values.ts @@ -44,13 +44,21 @@ export function filterByValues( } } else { const { fieldName, operator } = parseFilterProperty(k); + if (!operator) { + return filterByValuesOnGenericOperator({ + fieldName, + receivedValues, + jwtClaims, + value: v as Record, + }); + } const receivedValue = getReceivedValue({ fieldName, receivedValues, jwtClaims }); if (!receivedValue) { return false; } - const checkFilterPasses = getFilteringFn(operator); - if (!checkFilterPasses(receivedValue, v)) { + const filteringFn = getFilteringFn(operator); + if (!filteringFn(receivedValue, v)) { return false; } } @@ -58,6 +66,32 @@ export function filterByValues( return true; } +function filterByValuesOnGenericOperator({ + fieldName, + receivedValues, + jwtClaims, + value, +}: { + fieldName: string; + receivedValues: Record; + jwtClaims?: Map; + value: Record; +}): boolean { + const predicates = Object.entries(value).map(([k, v]) => { + const receivedValue = getReceivedValue({ fieldName, receivedValues, jwtClaims }); + if (!receivedValue) { + return false; + } + const filteringFn = getFilteringFn(k); + + if (!filteringFn(receivedValue, v)) { + return false; + } + return true; + }); + return multipleConditionsAggregationMap.AND(predicates); +} + function getReceivedValue({ fieldName, receivedValues, diff --git a/packages/graphql/src/translate/create-connect-and-params.test.ts b/packages/graphql/src/translate/create-connect-and-params.test.ts index 37d20ea2ab..403bed110f 100644 --- a/packages/graphql/src/translate/create-connect-and-params.test.ts +++ b/packages/graphql/src/translate/create-connect-and-params.test.ts @@ -46,7 +46,7 @@ describe("createConnectAndParams", () => { type: "`SIMILAR`", typeUnescaped: "SIMILAR", fieldName: "similarMovies", - queryDirection: RelationshipQueryDirectionOption.DEFAULT_DIRECTED, + queryDirection: RelationshipQueryDirectionOption.DIRECTED, aggregate: true, inherited: false, typeMeta: { @@ -138,7 +138,7 @@ describe("createConnectAndParams", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this0_node - MERGE (this)-[:\`SIMILAR\`]->(this0_node) + CREATE (this)-[:\`SIMILAR\`]->(this0_node) } } WITH this, this0_node @@ -153,7 +153,7 @@ describe("createConnectAndParams", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_node UNWIND connectedNodes as this0_node_similarMovies0_node - MERGE (this0_node)-[:\`SIMILAR\`]->(this0_node_similarMovies0_node) + CREATE (this0_node)-[:\`SIMILAR\`]->(this0_node_similarMovies0_node) } } WITH this, this0_node, this0_node_similarMovies0_node diff --git a/packages/graphql/src/translate/create-connect-and-params.ts b/packages/graphql/src/translate/create-connect-and-params.ts index abae9e5db2..d1b116c433 100644 --- a/packages/graphql/src/translate/create-connect-and-params.ts +++ b/packages/graphql/src/translate/create-connect-and-params.ts @@ -33,7 +33,6 @@ import { asArray } from "../utils/utils"; import { checkAuthentication } from "./authorization/check-authentication"; import { createAuthorizationAfterAndParams } from "./authorization/compatibility/create-authorization-after-and-params"; import { createAuthorizationBeforeAndParams } from "./authorization/compatibility/create-authorization-before-and-params"; -import { createRelationshipValidationString } from "./create-relationship-validation-string"; import { createSetRelationshipProperties } from "./create-set-relationship-properties"; import { filterMetaVariable } from "./subscriptions/filter-meta-variable"; import { createWhereNodePredicate } from "./where/create-where-predicate"; @@ -59,7 +58,6 @@ function createConnectAndParams({ callbackBucket, labelOverride, parentNode, - includeRelationshipValidation, isFirstLevel = true, source, indexPrefix, @@ -74,7 +72,6 @@ function createConnectAndParams({ refNodes: Node[]; labelOverride?: string; parentNode: Node; - includeRelationshipValidation?: boolean; isFirstLevel?: boolean; source: "CREATE" | "UPDATE" | "CONNECT"; indexPrefix?: string; @@ -95,7 +92,6 @@ function createConnectAndParams({ const inStr = relationField.direction === "IN" ? "<-" : "-"; const outStr = relationField.direction === "OUT" ? "->" : "-"; const relTypeStr = `[${relationField.properties ? relationshipName : ""}:${relationField.type}]`; - const isOverwriteNotAllowed = connect.overwrite === false; const subquery: string[] = []; const labels = relatedNode.getLabelString(context); @@ -174,8 +170,7 @@ function createConnectAndParams({ subquery.push("\t\t\tWITH connectedNodes, parentNodes"); // subquery.push(`\t\t\tUNWIND parentNodes as ${parentVar}`); subquery.push(`\t\t\tUNWIND connectedNodes as ${nodeName}`); - const connectOperator = isOverwriteNotAllowed ? "CREATE" : "MERGE"; - subquery.push(`\t\t\t${connectOperator} (${parentVar})${inStr}${relTypeStr}${outStr}(${nodeName})`); + subquery.push(`\t\t\tCREATE (${parentVar})${inStr}${relTypeStr}${outStr}(${nodeName})`); if (relationField.properties) { const relationship = context.relationships.find( @@ -215,31 +210,6 @@ function createConnectAndParams({ const innerMetaStr = ""; - if (includeRelationshipValidation || isOverwriteNotAllowed) { - const relValidationStrs: string[] = []; - const matrixItems = [ - [parentNode, parentVar], - [relatedNode, nodeName], - ] as [Node, string][]; - - matrixItems.forEach((mi) => { - const relValidationStr = createRelationshipValidationString({ - node: mi[0], - context, - varName: mi[1], - ...(isOverwriteNotAllowed && { relationshipFieldNotOverwritable: relationField.fieldName }), - }); - if (relValidationStr) { - relValidationStrs.push(relValidationStr); - } - }); - - if (relValidationStrs.length) { - subquery.push(`\tWITH ${[...filterMetaVariable(withVars), nodeName].join(", ")}${innerMetaStr}`); - subquery.push(relValidationStrs.join("\n")); - } - } - subquery.push(`WITH ${[...filterMetaVariable(withVars), nodeName].join(", ")}${innerMetaStr}`); if (connect.connect) { @@ -275,7 +245,6 @@ function createConnectAndParams({ refNodes: [newRefNode], parentNode: relatedNode, labelOverride: relField.union ? newRefNode.name : "", - includeRelationshipValidation: true, isFirstLevel: false, source: "CONNECT", }); diff --git a/packages/graphql/src/translate/create-connect-or-create-and-params.ts b/packages/graphql/src/translate/create-connect-or-create-and-params.ts deleted file mode 100644 index b019620914..0000000000 --- a/packages/graphql/src/translate/create-connect-or-create-and-params.ts +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Cypher from "@neo4j/cypher-builder"; -import type { Node, Relationship } from "../classes"; -import type { CallbackBucket } from "../classes/CallbackBucket"; -import type { PredicateReturn, PrimitiveField, RelationField } from "../types"; -import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; -import { getCypherRelationshipDirection } from "../utils/get-relationship-direction"; -import { asArray, omitFields } from "../utils/utils"; -import { checkAuthentication } from "./authorization/check-authentication"; -import { createAuthorizationAfterPredicate } from "./authorization/create-authorization-after-predicate"; -import { createAuthorizationBeforePredicate } from "./authorization/create-authorization-before-predicate"; -import { parseWhereField } from "./queryAST/factory/parsers/parse-where-field"; -import { assertNonAmbiguousUpdate } from "./utils/assert-non-ambiguous-update"; -import { addCallbackAndSetParamCypher } from "./utils/callback-utils"; - -type CreateOrConnectInput = { - where?: { - node: Record; - }; - onCreate?: { - node?: Record; - edge?: Record; - }; -}; - -export function createConnectOrCreateAndParams({ - input, - varName, - parentVar, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, -}: { - input: CreateOrConnectInput[] | CreateOrConnectInput; - varName: string; - parentVar: string; - relationField: RelationField; - refNode: Node; - node: Node; - context: Neo4jGraphQLTranslationContext; - withVars: string[]; - callbackBucket: CallbackBucket; -}): Cypher.CypherResult { - asArray(input).forEach((connectOrCreateItem) => { - assertNonAmbiguousUpdate(refNode, connectOrCreateItem.onCreate?.node ?? {}); - }); - - // todo: add create - checkAuthentication({ context, node, targetOperations: ["CREATE", "CREATE_RELATIONSHIP"] }); - checkAuthentication({ context, node: refNode, targetOperations: ["CREATE", "CREATE_RELATIONSHIP"] }); - - const withVarsVariables = withVars.map((name) => new Cypher.NamedVariable(name)); - - const statements = asArray(input).map((inputItem, index) => { - const subqueryBaseName = `${varName}${index}`; - const result = createConnectOrCreatePartialStatement({ - input: inputItem, - baseName: subqueryBaseName, - parentVar, - relationField, - refNode, - node, - context, - callbackBucket, - withVars, - }); - return result; - }); - - const wrappedQueries = statements.map((statement) => { - const returnStatement = new Cypher.Return([Cypher.count(new Cypher.Raw("*")), "_"]); - - const subqueryClause = new Cypher.With(...withVarsVariables) - .call(Cypher.utils.concat(statement, returnStatement)) - .importWith(...withVarsVariables); - - return subqueryClause; - }); - - const query = Cypher.utils.concat(...wrappedQueries); - return query.build({ prefix: `${varName}_` }); -} - -function createConnectOrCreatePartialStatement({ - input, - baseName, - parentVar, - relationField, - refNode, - node, - context, - callbackBucket, - withVars, -}: { - input: CreateOrConnectInput; - baseName: string; - parentVar: string; - relationField: RelationField; - refNode: Node; - node: Node; - context: Neo4jGraphQLTranslationContext; - callbackBucket: CallbackBucket; - withVars: string[]; -}): Cypher.Clause { - let mergeQuery: Cypher.CompositeClause | undefined; - - // TODO: connectOrCreate currently doesn't honour field-level authorization - this should be fixed - const authorizationBeforePredicateReturn = createAuthorizationBeforeConnectOrCreate({ - context, - sourceNode: node, - sourceName: parentVar, - targetNode: refNode, - targetName: baseName, - }); - - if (authorizationBeforePredicateReturn.predicate) { - if (authorizationBeforePredicateReturn.preComputedSubqueries) { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*"), - authorizationBeforePredicateReturn.preComputedSubqueries - ); - } - - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*").where(authorizationBeforePredicateReturn.predicate) - ); - } - - const mergeCypher = mergeStatement({ - input, - refNode, - context, - relationField, - parentNode: new Cypher.NamedNode(parentVar), - varName: baseName, - callbackBucket, - withVars, - }); - - mergeQuery = Cypher.utils.concat(mergeQuery, mergeCypher); - - const authorizationAfterPredicateReturn = createAuthorizationAfterConnectOrCreate({ - context, - sourceNode: node, - sourceName: parentVar, - targetNode: refNode, - targetName: baseName, - }); - - if (authorizationAfterPredicateReturn.predicate) { - if ( - authorizationAfterPredicateReturn.preComputedSubqueries && - !authorizationAfterPredicateReturn.preComputedSubqueries.empty - ) { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*"), - authorizationAfterPredicateReturn.preComputedSubqueries, - new Cypher.With("*").where(authorizationAfterPredicateReturn.predicate) - ); - } else { - mergeQuery = Cypher.utils.concat( - mergeQuery, - new Cypher.With("*").where(authorizationAfterPredicateReturn.predicate) - ); - } - } - - return mergeQuery; -} - -function mergeStatement({ - input, - refNode, - context, - relationField, - parentNode, - varName, - callbackBucket, -}: { - input: CreateOrConnectInput; - refNode: Node; - context: Neo4jGraphQLTranslationContext; - relationField: RelationField; - parentNode: Cypher.Node; - varName: string; - callbackBucket: CallbackBucket; - withVars: string[]; -}): Cypher.Clause { - const whereAttributes = Object.fromEntries( - Object.entries(input.where?.node ?? {}).map((whereAttribute) => { - const { fieldName } = parseWhereField(whereAttribute[0]); - return [fieldName, whereAttribute[1]]; - }) - ); - - const whereNodeParameters = getCypherParameters(whereAttributes, refNode); - const onCreateNodeParameters = getCypherParameters(input.onCreate?.node, refNode); - - const autogeneratedParams = getAutogeneratedParams(refNode); - const node = new Cypher.NamedNode(varName); - const nodePattern = new Cypher.Pattern(node, { - properties: whereNodeParameters, - labels: refNode.getLabels(context), - }); - - const unsetAutogeneratedParams = omitFields(autogeneratedParams, Object.keys(whereAttributes)); - const callbackFields = getCallbackFields(refNode); - - const callbackParams = callbackFields - .map((callbackField): [Cypher.Property, Cypher.Raw] | [] => { - const varNameVariable = new Cypher.NamedVariable(varName); - return addCallbackAndSetParamCypher( - callbackField, - varNameVariable, - parentNode, - callbackBucket, - "CREATE", - node - ); - }) - .filter((tuple) => tuple.length !== 0) as [Cypher.Property, Cypher.Raw][]; - - const rawNodeParams = { - ...unsetAutogeneratedParams, - ...onCreateNodeParameters, - }; - - const onCreateParams = Object.entries(rawNodeParams).map(([key, param]): [Cypher.Property, Cypher.Param] => { - return [node.property(key), param]; - }); - - const merge = new Cypher.Merge(nodePattern).onCreateSet(...onCreateParams, ...callbackParams); - - const relationshipFields = context.relationships.find((x) => x.properties === relationField.properties); - const autogeneratedRelationshipParams = relationshipFields ? getAutogeneratedParams(relationshipFields) : {}; - const rawOnCreateRelationshipParams = Cypher.utils.toCypherParams(input.onCreate?.edge || {}); - - const rawRelationshipParams = { - ...autogeneratedRelationshipParams, - ...rawOnCreateRelationshipParams, - }; - - const relationship = new Cypher.Relationship(); - const direction = getCypherRelationshipDirection(relationField); - const relationshipPattern = new Cypher.Pattern(parentNode) - .related(relationship, { type: relationField.type, direction }) - .to(node); - - const onCreateRelationshipParams = Object.entries(rawRelationshipParams).map( - ([key, param]): [Cypher.Property, Cypher.Param] => { - return [relationship.property(key), param]; - } - ); - const relationshipMerge = new Cypher.Merge(relationshipPattern).onCreateSet(...onCreateRelationshipParams); - - let withClause: Cypher.Clause | undefined; - - return Cypher.utils.concat(merge, relationshipMerge, withClause); -} - -function createAuthorizationBeforeConnectOrCreate({ - context, - sourceNode, - sourceName, -}: { - context: Neo4jGraphQLTranslationContext; - sourceNode: Node; - sourceName: string; - targetNode: Node; - targetName: string; -}): PredicateReturn { - const predicates: Cypher.Predicate[] = []; - let subqueries: Cypher.CompositeClause | undefined; - - const sourceAuthorizationBefore = createAuthorizationBeforePredicate({ - context, - nodes: [ - { - node: sourceNode, - variable: new Cypher.NamedNode(sourceName), - }, - ], - operations: ["CREATE_RELATIONSHIP"], - }); - - if (sourceAuthorizationBefore) { - const { predicate, preComputedSubqueries } = sourceAuthorizationBefore; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - return { - predicate: Cypher.and(...predicates), - preComputedSubqueries: subqueries, - }; -} - -function createAuthorizationAfterConnectOrCreate({ - context, - sourceNode, - sourceName, - targetNode, - targetName, -}: { - context: Neo4jGraphQLTranslationContext; - sourceNode: Node; - sourceName: string; - targetNode: Node; - targetName: string; -}): PredicateReturn { - const predicates: Cypher.Predicate[] = []; - let subqueries: Cypher.CompositeClause | undefined; - - const sourceAuthorizationAfter = createAuthorizationAfterPredicate({ - context, - nodes: [ - { - node: sourceNode, - variable: new Cypher.NamedNode(sourceName), - }, - ], - operations: ["CREATE_RELATIONSHIP"], - }); - - const targetAuthorizationAfter = createAuthorizationAfterPredicate({ - context, - nodes: [ - { - node: targetNode, - variable: new Cypher.NamedNode(targetName), - }, - ], - operations: ["CREATE_RELATIONSHIP", "CREATE"], - }); - - if (sourceAuthorizationAfter) { - const { predicate, preComputedSubqueries } = sourceAuthorizationAfter; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - if (targetAuthorizationAfter) { - const { predicate, preComputedSubqueries } = targetAuthorizationAfter; - - if (predicate) { - predicates.push(predicate); - } - - if (preComputedSubqueries) { - subqueries = Cypher.utils.concat(subqueries, preComputedSubqueries); - } - } - - return { - predicate: Cypher.and(...predicates), - preComputedSubqueries: subqueries, - }; -} - -function getCallbackFields(node: Node | Relationship): PrimitiveField[] { - const callbackFields = [ - ...node.primitiveFields.filter((f) => f.callback), - ...node.temporalFields.filter((f) => f.callback), - ]; - return callbackFields; -} - -// Helper for compatibility reasons -function getAutogeneratedParams(node: Node | Relationship): Record> { - const autogeneratedFields = node.primitiveFields - .filter((f) => f.autogenerate) - .reduce((acc, field) => { - if (field.dbPropertyName) { - acc[field.dbPropertyName] = new Cypher.Raw("randomUUID()"); - } - return acc; - }, {}); - - const autogeneratedTemporalFields = node.temporalFields - .filter((field) => ["DateTime", "Time"].includes(field.typeMeta.name) && field.timestamps?.includes("CREATE")) - .reduce((acc, field) => { - if (field.dbPropertyName) { - acc[field.dbPropertyName] = new Cypher.Raw(`${field.typeMeta.name.toLowerCase()}()`); - } - return acc; - }, {}); - return { ...autogeneratedTemporalFields, ...autogeneratedFields }; -} - -function getCypherParameters(onCreateParams: Record = {}, node?: Node): Record> { - const params = Object.entries(onCreateParams).reduce((acc, [key, value]) => { - const nodeField = node?.constrainableFields.find((f) => f.fieldName === key); - const nodeFieldName = nodeField?.dbPropertyNameUnescaped || nodeField?.fieldName; - const fieldName = nodeFieldName || key; - const valueOrArray = nodeField?.typeMeta.array ? asArray(value) : value; - acc[fieldName] = valueOrArray; - return acc; - }, {}); - return Cypher.utils.toCypherParams(params); -} diff --git a/packages/graphql/src/translate/create-create-and-params.ts b/packages/graphql/src/translate/create-create-and-params.ts index 9b485ac833..9553c901df 100644 --- a/packages/graphql/src/translate/create-create-and-params.ts +++ b/packages/graphql/src/translate/create-create-and-params.ts @@ -27,8 +27,6 @@ import { createAuthorizationAfterAndParamsField, } from "./authorization/compatibility/create-authorization-after-and-params"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; -import { createRelationshipValidationString } from "./create-relationship-validation-string"; import { createSetRelationshipProperties } from "./create-set-relationship-properties"; import { assertNonAmbiguousUpdate } from "./utils/assert-non-ambiguous-update"; import { addCallbackAndSetParam } from "./utils/callback-utils"; @@ -58,7 +56,6 @@ function createCreateAndParams({ context, callbackBucket, withVars, - includeRelationshipValidation, topLevelNodeVariable, authorizationPrefix = [0], }: { @@ -68,7 +65,6 @@ function createCreateAndParams({ context: Neo4jGraphQLTranslationContext; callbackBucket: CallbackBucket; withVars: string[]; - includeRelationshipValidation?: boolean; topLevelNodeVariable?: string; //used to build authorization variable in auth subqueries authorizationPrefix?: number[]; @@ -81,6 +77,7 @@ function createCreateAndParams({ const relationField = node.relationFields.find((x) => key === x.fieldName); const primitiveField = node.primitiveFields.find((x) => key === x.fieldName); const pointField = node.pointFields.find((x) => key === x.fieldName); + const temporalField = node.temporalFields.find((x) => key === x.fieldName); const dbFieldName = mapToDbProperty(node, key); if (primitiveField) { @@ -145,7 +142,6 @@ function createCreateAndParams({ node: refNode, varName: nodeName, withVars: [...withVars, nodeName], - includeRelationshipValidation: false, topLevelNodeVariable, authorizationPrefix: [...authorizationPrefix, reducerIndex, createIndex, refNodeIndex], }); @@ -185,16 +181,6 @@ function createCreateAndParams({ } res.meta.authorizationPredicates.push(...authorizationPredicates); } - - const relationshipValidationStr = createRelationshipValidationString({ - node: refNode, - context, - varName: nodeName, - }); - if (relationshipValidationStr) { - res.creates.push(`WITH *`); - res.creates.push(relationshipValidationStr); - } }); } @@ -216,22 +202,6 @@ function createCreateAndParams({ res.creates.push(connectAndParams[0]); res.params = { ...res.params, ...connectAndParams[1] }; } - - if (v.connectOrCreate) { - const { cypher, params } = createConnectOrCreateAndParams({ - input: v.connectOrCreate, - varName: `${varNameKey}${relationField.union ? "_" : ""}${unionTypeName}_connectOrCreate`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - res.creates.push(cypher); - res.params = { ...res.params, ...params }; - } }); if (relationField.interface && value.connect) { @@ -290,6 +260,22 @@ function createCreateAndParams({ return res; } + if (temporalField && ["DateTime", "Time"].includes(temporalField.typeMeta.name)) { + if (temporalField.typeMeta.array) { + res.creates.push( + `SET ${varName}.${dbFieldName} = [t in $${varNameKey} | ${temporalField.typeMeta.name.toLowerCase()}(t)]` + ); + } else { + res.creates.push( + `SET ${varName}.${dbFieldName} = ${temporalField.typeMeta.name.toLowerCase()}($${varNameKey})` + ); + } + + res.params[varNameKey] = value; + + return res; + } + res.creates.push(`SET ${varName}.${dbFieldName} = $${varNameKey}`); res.params[varNameKey] = value; @@ -348,15 +334,6 @@ function createCreateAndParams({ params = { ...params, ...authParams }; } - if (includeRelationshipValidation) { - const str = createRelationshipValidationString({ node, context, varName }); - - if (str) { - creates.push(`WITH *`); - creates.push(str); - } - } - return { create: creates.join("\n"), params, authorizationPredicates, authorizationSubqueries }; } diff --git a/packages/graphql/src/translate/create-delete-and-params.ts b/packages/graphql/src/translate/create-delete-and-params.ts index c262726080..716761922f 100644 --- a/packages/graphql/src/translate/create-delete-and-params.ts +++ b/packages/graphql/src/translate/create-delete-and-params.ts @@ -23,6 +23,7 @@ import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-tran import { caseWhere } from "../utils/case-where"; import { checkAuthentication } from "./authorization/check-authentication"; import { createAuthorizationBeforeAndParams } from "./authorization/compatibility/create-authorization-before-and-params"; +import { getRelationshipDirection } from "./utils/get-relationship-direction"; import createConnectionWhereAndParams from "./where/create-connection-where-and-params"; interface Res { @@ -77,8 +78,7 @@ function createDeleteAndParams({ refNodes.push(context.nodes.find((x) => x.name === relationField.typeMeta.name) as Node); } - const inStr = relationField.direction === "IN" ? "<-" : "-"; - const outStr = relationField.direction === "OUT" ? "->" : "-"; + const { inStr, outStr } = getRelationshipDirection(relationField); refNodes.forEach((refNode) => { checkAuthentication({ context, node: refNode, targetOperations: ["DELETE"] }); diff --git a/packages/graphql/src/translate/create-disconnect-and-params.test.ts b/packages/graphql/src/translate/create-disconnect-and-params.test.ts index c3fd5fe217..8e904d2b1b 100644 --- a/packages/graphql/src/translate/create-disconnect-and-params.test.ts +++ b/packages/graphql/src/translate/create-disconnect-and-params.test.ts @@ -39,7 +39,7 @@ describe("createDisconnectAndParams", () => { typeUnescaped: "SIMILAR", type: "`SIMILAR`", fieldName: "similarMovies", - queryDirection: RelationshipQueryDirectionOption.DEFAULT_DIRECTED, + queryDirection: RelationshipQueryDirectionOption.DIRECTED, aggregate: true, inherited: false, typeMeta: { diff --git a/packages/graphql/src/translate/create-disconnect-and-params.ts b/packages/graphql/src/translate/create-disconnect-and-params.ts index 47b8031437..70c69ca85f 100644 --- a/packages/graphql/src/translate/create-disconnect-and-params.ts +++ b/packages/graphql/src/translate/create-disconnect-and-params.ts @@ -25,6 +25,7 @@ import { caseWhere } from "../utils/case-where"; import { checkAuthentication } from "./authorization/check-authentication"; import { createAuthorizationAfterAndParams } from "./authorization/compatibility/create-authorization-after-and-params"; import { createAuthorizationBeforeAndParams } from "./authorization/compatibility/create-authorization-before-and-params"; +import { getRelationshipDirection } from "./utils/get-relationship-direction"; import createConnectionWhereAndParams from "./where/create-connection-where-and-params"; interface Res { @@ -67,8 +68,7 @@ function createDisconnectAndParams({ checkAuthentication({ context, node: relatedNode, targetOperations: ["DELETE_RELATIONSHIP"] }); const variableName = `${varName}${index}`; - const inStr = relationField.direction === "IN" ? "<-" : "-"; - const outStr = relationField.direction === "OUT" ? "->" : "-"; + const { inStr, outStr } = getRelationshipDirection(relationField); const relVarName = `${variableName}_rel`; const relTypeStr = `[${relVarName}:${relationField.type}]`; const subquery: string[] = []; diff --git a/packages/graphql/src/translate/create-relationship-validation-string.ts b/packages/graphql/src/translate/create-relationship-validation-string.ts deleted file mode 100644 index d9a5171fb0..0000000000 --- a/packages/graphql/src/translate/create-relationship-validation-string.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Node } from "../classes"; -import { RELATIONSHIP_REQUIREMENT_PREFIX } from "../constants"; -import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; - -export function createRelationshipValidationString({ - node, - context, - varName, - relationshipFieldNotOverwritable, -}: { - node: Node; - context: Neo4jGraphQLTranslationContext; - varName: string; - relationshipFieldNotOverwritable?: string; -}): string { - const strs: string[] = []; - - node.relationFields.forEach((field) => { - const isArray = field.typeMeta.array; - const isUnionOrInterface = Boolean(field.union) || Boolean(field.interface); - if (isUnionOrInterface) { - return; - } - - const toNode = context.nodes.find((n) => n.name === field.typeMeta.name) as Node; - const inStr = field.direction === "IN" ? "<-" : "-"; - const outStr = field.direction === "OUT" ? "->" : "-"; - const relVarname = `${varName}_${field.fieldName}_${toNode.name}_unique`; - - let predicate: string; - let errorMsg: string; - let subQuery: string | undefined; - if (isArray) { - if (relationshipFieldNotOverwritable === field.fieldName) { - predicate = `c = 1`; - errorMsg = `${RELATIONSHIP_REQUIREMENT_PREFIX}${node.name}.${field.fieldName} required exactly once for a specific ${toNode.name}`; - subQuery = [ - `CALL {`, - `\tWITH ${varName}`, - `\tMATCH (${varName})${inStr}[${relVarname}:${field.type}]${outStr}(other${toNode.getLabelString( - context - )})`, - `\tWITH count(${relVarname}) as c, other`, - `\tWHERE apoc.util.validatePredicate(NOT (${predicate}), '${errorMsg}', [0])`, - `\tRETURN collect(c) AS ${relVarname}_ignored`, - `}`, - ].join("\n"); - } - } else { - predicate = `c = 1`; - errorMsg = `${RELATIONSHIP_REQUIREMENT_PREFIX}${node.name}.${field.fieldName} required exactly once`; - if (!field.typeMeta.required) { - predicate = `c <= 1`; - errorMsg = `${RELATIONSHIP_REQUIREMENT_PREFIX}${node.name}.${field.fieldName} must be less than or equal to one`; - } - - subQuery = [ - `CALL {`, - `\tWITH ${varName}`, - `\tMATCH (${varName})${inStr}[${relVarname}:${field.type}]${outStr}(${toNode.getLabelString(context)})`, - `\tWITH count(${relVarname}) as c`, - `\tWHERE apoc.util.validatePredicate(NOT (${predicate}), '${errorMsg}', [0])`, - `\tRETURN c AS ${relVarname}_ignored`, - `}`, - ].join("\n"); - } - - if (subQuery) { - strs.push(subQuery); - } - }); - - return strs.join("\n"); -} diff --git a/packages/graphql/src/translate/create-set-relationship-properties.ts b/packages/graphql/src/translate/create-set-relationship-properties.ts index 3d88ed0703..62d8492c51 100644 --- a/packages/graphql/src/translate/create-set-relationship-properties.ts +++ b/packages/graphql/src/translate/create-set-relationship-properties.ts @@ -34,6 +34,7 @@ export function createSetRelationshipProperties({ callbackBucket, parameterPrefix, parameterNotation, + isUpdateOperation = false, }: { properties: Record>; varName: string; @@ -44,6 +45,7 @@ export function createSetRelationshipProperties({ callbackBucket: CallbackBucket; parameterPrefix: string; parameterNotation: "." | "_"; + isUpdateOperation?: boolean; }): [string, Record] | undefined { // setting properties on the edge of an Interface relationship // the input can contain other properties than the one applicable for this concrete entity relationship field @@ -59,6 +61,7 @@ export function createSetRelationshipProperties({ callbackBucket, parameterPrefix: `${parameterPrefix}${parameterNotation}${relationship.properties}`, parameterNotation, + isUpdateOperation, }); } return; @@ -72,6 +75,7 @@ export function createSetRelationshipProperties({ callbackBucket, parameterPrefix, parameterNotation, + isUpdateOperation, }); } @@ -84,6 +88,7 @@ function createSetRelationshipPropertiesForProperties({ callbackBucket, parameterPrefix, parameterNotation, + isUpdateOperation, }: { properties: Record; varName: string; @@ -93,6 +98,7 @@ function createSetRelationshipPropertiesForProperties({ callbackBucket: CallbackBucket; parameterPrefix: string; parameterNotation: "." | "_"; + isUpdateOperation: boolean; }): [string, Record] { assertNonAmbiguousUpdate(relationship, properties); const strs: string[] = []; @@ -112,6 +118,7 @@ function createSetRelationshipPropertiesForProperties({ varName, value, withVars, + isUpdateOperation, }); strs.push(mutationFieldStatements); params[param] = value; diff --git a/packages/graphql/src/translate/create-update-and-params.test.ts b/packages/graphql/src/translate/create-update-and-params.test.ts index bad09e8b5d..42e83d1e26 100644 --- a/packages/graphql/src/translate/create-update-and-params.test.ts +++ b/packages/graphql/src/translate/create-update-and-params.test.ts @@ -17,16 +17,16 @@ * limitations under the License. */ -import createUpdateAndParams from "./create-update-and-params"; -import { CallbackBucket } from "../classes/CallbackBucket"; -import type { BaseField } from "../types"; -import { trimmer } from "../utils"; -import { NodeBuilder } from "../../tests/utils/builders/node-builder"; import { ContextBuilder } from "../../tests/utils/builders/context-builder"; +import { NodeBuilder } from "../../tests/utils/builders/node-builder"; +import { CallbackBucket } from "../classes/CallbackBucket"; import { Neo4jGraphQLSchemaModel } from "../schema-model/Neo4jGraphQLSchemaModel"; -import { ConcreteEntity } from "../schema-model/entity/ConcreteEntity"; import { Attribute } from "../schema-model/attribute/Attribute"; import { GraphQLBuiltInScalarType, ScalarType } from "../schema-model/attribute/AttributeType"; +import { ConcreteEntity } from "../schema-model/entity/ConcreteEntity"; +import type { BaseField } from "../types"; +import { trimmer } from "../utils"; +import createUpdateAndParams from "./create-update-and-params"; describe("createUpdateAndParams", () => { test("should return the correct update and params", () => { @@ -96,7 +96,7 @@ describe("createUpdateAndParams", () => { }).instance(); const result = createUpdateAndParams({ - updateInput: { id: "new" }, + updateInput: { id_SET: "new" }, node, context, varName: "this", @@ -108,12 +108,12 @@ describe("createUpdateAndParams", () => { expect(trimmer(result[0])).toEqual( trimmer(` - SET this.id = $this_update_id + SET this.id = $this_update_id_SET `) ); expect(result[1]).toMatchObject({ - this_update_id: "new", + this_update_id_SET: "new", }); }); }); diff --git a/packages/graphql/src/translate/create-update-and-params.ts b/packages/graphql/src/translate/create-update-and-params.ts index f5eba797bf..67b8a012fc 100644 --- a/packages/graphql/src/translate/create-update-and-params.ts +++ b/packages/graphql/src/translate/create-update-and-params.ts @@ -35,16 +35,15 @@ import { createAuthorizationBeforeAndParamsField, } from "./authorization/compatibility/create-authorization-before-and-params"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; import createCreateAndParams from "./create-create-and-params"; import createDeleteAndParams from "./create-delete-and-params"; import createDisconnectAndParams from "./create-disconnect-and-params"; -import { createRelationshipValidationString } from "./create-relationship-validation-string"; import { createSetRelationshipProperties } from "./create-set-relationship-properties"; import { assertNonAmbiguousUpdate } from "./utils/assert-non-ambiguous-update"; import { addCallbackAndSetParam } from "./utils/callback-utils"; import { getAuthorizationStatements } from "./utils/get-authorization-statements"; import { getMutationFieldStatements } from "./utils/get-mutation-field-statements"; +import { getRelationshipDirection } from "./utils/get-relationship-direction"; import { indentBlock } from "./utils/indent-block"; import { parseMutableField } from "./utils/parse-mutable-field"; import createConnectionWhereAndParams from "./where/create-connection-where-and-params"; @@ -73,7 +72,6 @@ export default function createUpdateAndParams({ context, callbackBucket, parameterPrefix, - includeRelationshipValidation, }: { parentVar: string; updateInput: any; @@ -84,7 +82,6 @@ export default function createUpdateAndParams({ context: Neo4jGraphQLTranslationContext; callbackBucket: CallbackBucket; parameterPrefix: string; - includeRelationshipValidation?: boolean; }): [string, any] { let hasAppliedTimeStamps = false; @@ -121,8 +118,7 @@ export default function createUpdateAndParams({ refNodes.push(context.nodes.find((x) => x.name === relationField.typeMeta.name) as Node); } - const inStr = relationField.direction === "IN" ? "<-" : "-"; - const outStr = relationField.direction === "OUT" ? "->" : "-"; + const { inStr, outStr } = getRelationshipDirection(relationField); const subqueries: string[] = []; const intermediateWithMetaStatements: string[] = []; @@ -276,6 +272,7 @@ export default function createUpdateAndParams({ relationField.union ? `.${refNode.name}` : "" }${relationField.typeMeta.array ? `[${index}]` : ``}.update.edge`, parameterNotation: ".", + isUpdateOperation: true, }); let setProperties; if (res) { @@ -306,7 +303,6 @@ export default function createUpdateAndParams({ parameterPrefix: `${parameterPrefix}.${key}${ relationField.union ? `.${refNode.name}` : "" }${relationField.typeMeta.array ? `[${index}]` : ``}.update.node`, - includeRelationshipValidation: true, }); res.params = { ...res.params, ...updateAndParams[1] }; innerUpdate.push(updateAndParams[0]); @@ -365,22 +361,6 @@ export default function createUpdateAndParams({ res.params = { ...res.params, ...connectAndParams[1] }; } - if (update.connectOrCreate) { - const { cypher, params } = createConnectOrCreateAndParams({ - input: update.connectOrCreate, - varName: `${variableName}_connectOrCreate`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - subquery.push(cypher); - res.params = { ...res.params, ...params }; - } - if (update.create) { if (withVars) { subquery.push(`WITH ${withVars.join(", ")}`); @@ -445,7 +425,6 @@ export default function createUpdateAndParams({ callbackBucket, varName: nodeName, withVars: [...withVars, nodeName], - includeRelationshipValidation: false, ...createNodeInput, }); subquery.push(nestedCreate); @@ -481,16 +460,6 @@ export default function createUpdateAndParams({ subquery.push( ...getAuthorizationStatements(authorizationPredicates, authorizationSubqueries) ); - - const relationshipValidationStr = createRelationshipValidationString({ - node: refNode, - context, - varName: nodeName, - }); - if (relationshipValidationStr) { - subquery.push(`WITH ${[...withVars, nodeName].join(", ")}`); - subquery.push(relationshipValidationStr); - } }); } @@ -554,6 +523,7 @@ export default function createUpdateAndParams({ varName, value, withVars, + isUpdateOperation: true, }); res.strs.push(mutationFieldStatements); @@ -641,11 +611,6 @@ export default function createUpdateAndParams({ const preUpdatePredicates = authorizationBeforeStrs; - const preArrayMethodValidationStr = ""; - const relationshipValidationStr = includeRelationshipValidation - ? createRelationshipValidationString({ node, context, varName }) - : ""; - if (meta.preArrayMethodValidationStrs.length) { const nullChecks = meta.preArrayMethodValidationStrs.map((validationStr) => `${validationStr[0]} IS NULL`); const propertyNames = meta.preArrayMethodValidationStrs.map((validationStr) => validationStr[1]); @@ -685,16 +650,7 @@ export default function createUpdateAndParams({ const statements = strs; - return [ - [ - preUpdatePredicatesStr, - preArrayMethodValidationStr, - ...statements, - authorizationAfterStr, - ...(relationshipValidationStr ? [withStr, relationshipValidationStr] : []), - ].join("\n"), - params, - ]; + return [[preUpdatePredicatesStr, ...statements, authorizationAfterStr].join("\n"), params]; } function validateNonNullProperty(res: Res, varName: string, field: BaseField) { diff --git a/packages/graphql/src/translate/queryAST/ast/fields/OperationField.ts b/packages/graphql/src/translate/queryAST/ast/fields/OperationField.ts index 7e9ad1bdef..5f9181af56 100644 --- a/packages/graphql/src/translate/queryAST/ast/fields/OperationField.ts +++ b/packages/graphql/src/translate/queryAST/ast/fields/OperationField.ts @@ -20,8 +20,8 @@ import Cypher from "@neo4j/cypher-builder"; import { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; -import { CypherOperation } from "../operations/CypherOperation"; -import { CypherScalarOperation } from "../operations/CypherScalarOperation"; +import { CypherAttributeOperation } from "../operations/CypherAttributeOperation"; +import { CypherEntityOperation } from "../operations/CypherEntityOperation"; import { CompositeCypherOperation } from "../operations/composite/CompositeCypherOperation"; import type { Operation } from "../operations/operations"; import { Field } from "./Field"; @@ -55,11 +55,11 @@ export class OperationField extends Field { } public isCypherField(): this is this & { - operation: CypherOperation | CypherScalarOperation | CompositeCypherOperation; + operation: CypherEntityOperation | CypherAttributeOperation | CompositeCypherOperation; } { return ( - this.operation instanceof CypherOperation || - this.operation instanceof CypherScalarOperation || + this.operation instanceof CypherEntityOperation || + this.operation instanceof CypherAttributeOperation || this.operation instanceof CompositeCypherOperation ); } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/ConnectionFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/ConnectionFilter.ts index 1e1aa53656..135336c333 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/ConnectionFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/ConnectionFilter.ts @@ -35,8 +35,6 @@ export class ConnectionFilter extends Filter { protected relationship: RelationshipAdapter; protected target: ConcreteEntityAdapter | InterfaceEntityAdapter; // target can be an interface entity, only with the label predicate optimization protected operator: RelationshipWhereOperator; - protected isNot: boolean; - // Predicate generation for subqueries cannot be done separately from subqueries, so we need to create the predicates at the same time // as subqueries and store them protected subqueryPredicate: Cypher.Predicate | undefined; @@ -45,17 +43,14 @@ export class ConnectionFilter extends Filter { relationship, target, operator, - isNot, }: { relationship: RelationshipAdapter; target: ConcreteEntityAdapter | InterfaceEntityAdapter; - operator: RelationshipWhereOperator | undefined; - isNot: boolean; + operator: RelationshipWhereOperator; }) { super(); this.relationship = relationship; - this.isNot = isNot; - this.operator = operator || "SOME"; + this.operator = operator; this.target = target; } @@ -72,7 +67,9 @@ export class ConnectionFilter extends Filter { } public getSubqueries(context: QueryASTContext): Cypher.Clause[] { - if (!hasTarget(context)) throw new Error("No parent node found!"); + if (!hasTarget(context)) { + throw new Error("No parent node found!"); + } const targetNode = new Cypher.Node(); const targetLabels = getEntityLabels(this.target, context.neo4jGraphQLContext); const relationship = new Cypher.Relationship(); @@ -98,7 +95,9 @@ export class ConnectionFilter extends Filter { } public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { - if (!hasTarget(queryASTContext)) throw new Error("No parent node found!"); + if (!hasTarget(queryASTContext)) { + throw new Error("No parent node found!"); + } if (this.subqueryPredicate) { return this.subqueryPredicate; } @@ -120,10 +119,7 @@ export class ConnectionFilter extends Filter { const nestedContext = queryASTContext.push({ target, relationship }); - const predicate = this.createRelationshipOperation(pattern, nestedContext); - if (predicate) { - return this.wrapInNotIfNeeded(predicate); - } + return this.createRelationshipOperation(pattern, nestedContext); } /** * Create a label predicate that filters concrete entities for interface target, @@ -137,8 +133,12 @@ export class ConnectionFilter extends Filter { * RETURN this { .name } AS this **/ protected getLabelPredicate(context: QueryASTContext): Cypher.Predicate | undefined { - if (!hasTarget(context)) throw new Error("No parent node found!"); - if (isConcreteEntity(this.target)) return undefined; + if (!hasTarget(context)) { + throw new Error("No parent node found!"); + } + if (isConcreteEntity(this.target)) { + return; + } const labelPredicate = this.target.concreteEntities.map((e) => { return context.target.hasLabels(...e.labels); }); @@ -153,7 +153,9 @@ export class ConnectionFilter extends Filter { const labelPredicate = this.getLabelPredicate(queryASTContext); const innerPredicate = Cypher.and(...connectionFilter, labelPredicate); - if (!innerPredicate) return undefined; + if (!innerPredicate) { + return; + } switch (this.operator) { case "ALL": { @@ -166,11 +168,12 @@ export class ConnectionFilter extends Filter { return this.createSingleRelationshipOperation(pattern, queryASTContext, innerPredicate); } default: { - if (!this.relationship.isList) { - return this.createSingleRelationshipOperation(pattern, queryASTContext, innerPredicate); - } const match = new Cypher.Match(pattern).where(innerPredicate); - return new Cypher.Exists(match); + const existsClause = new Cypher.Exists(match); + if (this.operator === "NONE") { + return Cypher.not(existsClause); + } + return existsClause; } } } @@ -180,7 +183,9 @@ export class ConnectionFilter extends Filter { context: QueryASTContext, innerPredicate: Cypher.Predicate ) { - if (!hasTarget(context)) throw new Error("No parent node found!"); + if (!hasTarget(context)) { + throw new Error("No parent node found!"); + } const patternComprehension = new Cypher.PatternComprehension(pattern) .map(new Cypher.Literal(1)) .where(innerPredicate); @@ -191,7 +196,9 @@ export class ConnectionFilter extends Filter { pattern: Cypher.Pattern, queryASTContext: QueryASTContext ): Cypher.Clause[] { - if (!hasTarget(queryASTContext)) throw new Error("No parent node found!"); + if (!hasTarget(queryASTContext)) { + throw new Error("No parent node found!"); + } const match = new Cypher.Match(pattern); const returnVar = new Cypher.Variable(); const innerFiltersPredicates: Cypher.Predicate[] = []; @@ -213,7 +220,7 @@ export class ConnectionFilter extends Filter { if (subqueries.length === 0) return []; // Hack logic to change predicates logic - const comparisonValue = this.isNot ? Cypher.false : Cypher.true; + const comparisonValue = this.operator === "NONE" ? Cypher.false : Cypher.true; this.subqueryPredicate = Cypher.eq(returnVar, comparisonValue); const countComparisonPredicate = @@ -231,7 +238,9 @@ export class ConnectionFilter extends Filter { // 1. "All" operations require 2 CALL subqueries // 2. Each subquery has its own return variable, that needs to be carried over to the predicate private getSubqueriesForOperationAll(pattern: Cypher.Pattern, queryASTContext: QueryASTContext): Cypher.Clause[] { - if (!hasTarget(queryASTContext)) throw new Error("No parent node found!"); + if (!hasTarget(queryASTContext)) { + throw new Error("No parent node found!"); + } const match = new Cypher.Match(pattern); const match2 = new Cypher.Match(pattern); @@ -255,7 +264,9 @@ export class ConnectionFilter extends Filter { return nestedSubqueries; }); - if (subqueries.length === 0) return []; + if (subqueries.length === 0) { + return []; + } const subqueries2 = this.innerFilters.flatMap((f) => { const nestedSubqueries = f.getSubqueries(queryASTContext).map((sq) => { @@ -280,9 +291,4 @@ export class ConnectionFilter extends Filter { return [Cypher.utils.concat(match, ...subqueries), Cypher.utils.concat(match2, ...subqueries2)]; } - - private wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - if (this.isNot) return Cypher.not(predicate); - else return predicate; - } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/CypherOneToOneRelationshipFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/CypherOneToOneRelationshipFilter.ts index d326464f8b..d9c691cc1d 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/CypherOneToOneRelationshipFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/CypherOneToOneRelationshipFilter.ts @@ -31,28 +31,24 @@ export class CypherOneToOneRelationshipFilter extends Filter { private selection: CustomCypherSelection; private operator: FilterOperator; private targetNodeFilters: Filter[] = []; - private isNot: boolean; private isNull: boolean; constructor({ selection, attribute, operator, - isNot, isNull, returnVariable, }: { selection: CustomCypherSelection; attribute: AttributeAdapter; operator: RelationshipWhereOperator; - isNot: boolean; isNull: boolean; returnVariable: Cypher.Node; }) { super(); this.selection = selection; this.attribute = attribute; - this.isNot = isNot; this.isNull = isNull; this.operator = operator; this.returnVariable = returnVariable; @@ -67,7 +63,7 @@ export class CypherOneToOneRelationshipFilter extends Filter { } public print(): string { - return `${super.print()} [${this.attribute.name}] <${this.isNot ? "NOT " : ""}${this.operator}>`; + return `${super.print()} [${this.attribute.name}] <${this.operator}>`; } public getSubqueries(context: QueryASTContext): Cypher.Clause[] { @@ -84,10 +80,7 @@ export class CypherOneToOneRelationshipFilter extends Filter { public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { const context = queryASTContext.setTarget(this.returnVariable); - const predicate = this.createRelationshipOperation(context); - if (predicate) { - return this.wrapInNotIfNeeded(predicate); - } + return this.createRelationshipOperation(context); } private createRelationshipOperation(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { @@ -100,12 +93,4 @@ export class CypherOneToOneRelationshipFilter extends Filter { return innerPredicate; } - - private wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - if (this.isNot) { - return Cypher.not(predicate); - } - - return predicate; - } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/CypherRelationshipFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/CypherRelationshipFilter.ts index f5e831b34a..49b8469b27 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/CypherRelationshipFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/CypherRelationshipFilter.ts @@ -31,25 +31,21 @@ export class CypherRelationshipFilter extends Filter { private selection: CustomCypherSelection; private operator: FilterOperator; private targetNodeFilters: Filter[] = []; - private isNot: boolean; constructor({ selection, attribute, operator, - isNot, returnVariable, }: { selection: CustomCypherSelection; attribute: AttributeAdapter; operator: RelationshipWhereOperator; - isNot: boolean; returnVariable: Cypher.Node; }) { super(); this.selection = selection; this.attribute = attribute; - this.isNot = isNot; this.operator = operator; this.returnVariable = returnVariable; } @@ -63,7 +59,7 @@ export class CypherRelationshipFilter extends Filter { } public print(): string { - return `${super.print()} [${this.attribute.name}] <${this.isNot ? "NOT " : ""}${this.operator}>`; + return `${super.print()} [${this.attribute.name}] <${this.operator}>`; } public getSubqueries(context: QueryASTContext): Cypher.Clause[] { @@ -77,10 +73,7 @@ export class CypherRelationshipFilter extends Filter { public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { const context = queryASTContext.setTarget(this.returnVariable); - const predicate = this.createRelationshipOperation(context); - if (predicate) { - return this.wrapInNotIfNeeded(predicate); - } + return this.createRelationshipOperation(context); } private createRelationshipOperation(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { @@ -108,13 +101,4 @@ export class CypherRelationshipFilter extends Filter { } } } - - private wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - // TODO: Remove check for NONE when isNot is removed - if (this.isNot && this.operator !== "NONE") { - return Cypher.not(predicate); - } - - return predicate; - } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/Filter.ts b/packages/graphql/src/translate/queryAST/ast/filters/Filter.ts index 201a5b2316..606c2e3738 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/Filter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/Filter.ts @@ -30,19 +30,21 @@ export type RelationshipWhereOperator = "ALL" | "NONE" | "SINGLE" | "SOME"; export type FilterOperator = | "EQ" - | "NOT" | NumericalWhereOperator | SpatialWhereOperator | StringWhereOperator - | `NOT_${StringWhereOperator}` | RegexWhereOperator | ArrayWhereOperator - | `NOT_${ArrayWhereOperator}` | RelationshipWhereOperator; export type LogicalOperators = "NOT" | "AND" | "OR" | "XOR"; -const RELATIONSHIP_OPERATORS = ["ALL", "NONE", "SINGLE", "SOME"] as const; +const LEGACY_RELATIONSHIP_OPERATORS = ["ALL", "NONE", "SINGLE", "SOME"] as const; +const RELATIONSHIP_OPERATORS = ["all", "none", "single", "some"] as const; + +export function isLegacyRelationshipOperator(operator: string): operator is RelationshipWhereOperator { + return LEGACY_RELATIONSHIP_OPERATORS.includes(operator as any); +} export function isRelationshipOperator(operator: string): operator is RelationshipWhereOperator { return RELATIONSHIP_OPERATORS.includes(operator as any); diff --git a/packages/graphql/src/translate/queryAST/ast/filters/RelationshipFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/RelationshipFilter.ts index cdd75fac80..d89e82a48a 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/RelationshipFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/RelationshipFilter.ts @@ -34,7 +34,6 @@ export class RelationshipFilter extends Filter { protected targetNodeFilters: Filter[] = []; protected relationship: RelationshipAdapter; protected operator: RelationshipWhereOperator; - protected isNot: boolean; protected target: ConcreteEntityAdapter | InterfaceEntityAdapter; // TODO: remove this, this is not good @@ -46,17 +45,14 @@ export class RelationshipFilter extends Filter { constructor({ relationship, operator, - isNot, target, }: { relationship: RelationshipAdapter; operator: RelationshipWhereOperator; - isNot: boolean; target: ConcreteEntityAdapter | InterfaceEntityAdapter; }) { super(); this.relationship = relationship; - this.isNot = isNot; this.operator = operator; this.target = target; } @@ -70,7 +66,7 @@ export class RelationshipFilter extends Filter { } public print(): string { - return `${super.print()} [${this.relationship.name}] <${this.isNot ? "NOT " : ""}${this.operator}>`; + return `${super.print()} [${this.relationship.name}] <${this.operator}>`; } @Memoize() @@ -94,7 +90,9 @@ export class RelationshipFilter extends Filter { throw new Error("No parent node found!"); } const selection = f.getSelection(context); - if (selection.length === 0) return undefined; + if (selection.length === 0) { + return; + } const pattern = new Cypher.Pattern(context.source) .related({ @@ -184,9 +182,7 @@ export class RelationshipFilter extends Filter { const subqueriesPredicate = Cypher.and(...subqueriesFilters); - // NOTE: NONE is SOME + isNot - // TODO: move to wrapInNullIfNeeded in getPredicate - const comparator = this.isNot ? Cypher.false : Cypher.true; + const comparator = this.operator === "NONE" ? Cypher.false : Cypher.true; this.subqueryPredicate = Cypher.eq(returnVar, comparator); const withAfterSubqueries = new Cypher.With("*"); @@ -263,11 +259,7 @@ export class RelationshipFilter extends Filter { switch (this.operator) { case "NONE": case "SOME": - if (this.relationship.isList) { - return Cypher.gt(Cypher.count(target), new Cypher.Literal(0)); - } else { - return Cypher.eq(Cypher.count(target), new Cypher.Literal(1)); - } + return Cypher.gt(Cypher.count(target), new Cypher.Literal(0)); case "SINGLE": return Cypher.eq(Cypher.count(target), new Cypher.Literal(1)); case "ALL": @@ -275,48 +267,12 @@ export class RelationshipFilter extends Filter { } } - protected shouldCreateOptionalMatch(): boolean { - return !this.relationship.isList && !this.relationship.isNullable; - } - - public getSelection(queryASTContext: QueryASTContext): Array { - if (this.shouldCreateOptionalMatch() && !this.subqueryPredicate) { - const nestedContext = this.getNestedContext(queryASTContext); - if (!nestedContext.hasTarget()) { - throw new Error("No parent node found!"); - } - - const pattern = new Cypher.Pattern(nestedContext.source) - .related({ - type: this.relationship.type, - direction: this.relationship.getCypherDirection(), - }) - .to(nestedContext.target, { - labels: getEntityLabels(this.target, nestedContext.neo4jGraphQLContext), - }); - return [ - new Cypher.OptionalMatch(pattern).with("*", [Cypher.count(nestedContext.target), this.countVariable]), - ]; - } - return []; - } - public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { if (this.subqueryPredicate) { return this.subqueryPredicate; } const nestedContext = this.getNestedContext(queryASTContext); - if (this.shouldCreateOptionalMatch()) { - const predicates = this.targetNodeFilters.map((c) => c.getPredicate(nestedContext)); - const innerPredicate = Cypher.and(...predicates); - if (this.isNot) { - return Cypher.and(Cypher.eq(this.countVariable, new Cypher.Literal(0)), innerPredicate); - } else { - return Cypher.and(Cypher.neq(this.countVariable, new Cypher.Literal(0)), innerPredicate); - } - } - const pattern = new Cypher.Pattern(nestedContext.source as Cypher.Node) .related({ type: this.relationship.type, @@ -327,9 +283,7 @@ export class RelationshipFilter extends Filter { }); const predicate = this.createRelationshipOperation(pattern, nestedContext); - if (predicate) { - return this.wrapInNotIfNeeded(predicate); - } + return predicate; } protected getSingleRelationshipOperation({ @@ -382,25 +336,15 @@ export class RelationshipFilter extends Filter { case "SOME": { const match = new Cypher.Match(pattern); if (innerPredicate) { - if (!this.relationship.isList) { - return this.getSingleRelationshipOperation({ - pattern, - queryASTContext, - innerPredicate, - }); - } - return new Cypher.Exists(match.where(innerPredicate)); + match.where(innerPredicate); } - return new Cypher.Exists(match); + const exists = new Cypher.Exists(match); + if (this.operator === "NONE") { + return Cypher.not(exists); + } + return exists; } } } - - protected wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - if (this.isNot) { - return Cypher.not(predicate); - } - return predicate; - } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts new file mode 100644 index 0000000000..bd97e336c0 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; + +export class AggregationDateTimeFilter extends AggregationPropertyFilter { + protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { + return this.createDateTimeOperation({ + operator: this.logicalOperator, + property: expr, + param: new Cypher.Param(this.comparisonValue), + }); + } + + private createDateTimeOperation({ + operator, + property, + param, + }: { + operator: AggregationLogicalOperator; + property: Cypher.Expr; + param: Cypher.Expr; + }) { + const variable = Cypher.datetime(param); + + return this.createBaseOperation({ + operator, + property, + param: variable, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts index 8036adc709..a6d1059ad5 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts @@ -18,8 +18,8 @@ */ import Cypher from "@neo4j/cypher-builder"; -import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; export class AggregationDurationFilter extends AggregationPropertyFilter { protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts new file mode 100644 index 0000000000..a37464df0f --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; + +export class AggregationTimeFilter extends AggregationPropertyFilter { + protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { + return this.createTimeOperation({ + operator: this.logicalOperator, + property: expr, + param: new Cypher.Param(this.comparisonValue), + }); + } + + private createTimeOperation({ + operator, + property, + param, + }: { + operator: AggregationLogicalOperator; + property: Cypher.Expr; + param: Cypher.Expr; + }) { + const variable = Cypher.time(param); + + return this.createBaseOperation({ + operator, + property, + param: variable, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/CountFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/CountFilter.ts index 2ef6ab01d9..500e3ab024 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/CountFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/CountFilter.ts @@ -18,30 +18,27 @@ */ import Cypher from "@neo4j/cypher-builder"; -import type { FilterOperator } from "../Filter"; -import { Filter } from "../Filter"; +import { hasTarget } from "../../../utils/context-has-target"; import type { QueryASTContext } from "../../QueryASTContext"; import type { QueryASTNode } from "../../QueryASTNode"; -import { hasTarget } from "../../../utils/context-has-target"; +import type { FilterOperator } from "../Filter"; +import { Filter } from "../Filter"; export class CountFilter extends Filter { protected comparisonValue: unknown; protected operator: FilterOperator; - protected isNot: boolean; // _NOT is deprecated constructor({ - isNot, operator, comparisonValue, }: { operator: FilterOperator; - isNot: boolean; + comparisonValue: unknown; }) { super(); this.comparisonValue = comparisonValue; this.operator = operator; - this.isNot = isNot; } public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { @@ -58,7 +55,7 @@ export class CountFilter extends Filter { } public print(): string { - return `${super.print()} <${this.isNot ? "NOT " : ""}${this.operator}>`; + return `${super.print()} <${this.operator}>`; } /** Returns the default operation for a given filter */ diff --git a/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/AuthRelationshipFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/AuthRelationshipFilter.ts index 0c0f06b2ee..8c5ed6f6c7 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/AuthRelationshipFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/AuthRelationshipFilter.ts @@ -24,14 +24,10 @@ import { RelationshipFilter } from "../RelationshipFilter"; export class AuthRelationshipFilter extends RelationshipFilter { public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate | undefined { - if (this.subqueryPredicate) return this.subqueryPredicate; - const nestedContext = this.getNestedContext(queryASTContext); - - if (this.shouldCreateOptionalMatch()) { - const predicates = this.targetNodeFilters.map((c) => c.getPredicate(nestedContext)); - const innerPredicate = Cypher.and(...predicates); - return Cypher.and(Cypher.neq(this.countVariable, new Cypher.Literal(0)), innerPredicate); + if (this.subqueryPredicate) { + return this.subqueryPredicate; } + const nestedContext = this.getNestedContext(queryASTContext); const pattern = new Cypher.Pattern(nestedContext.source as Cypher.Node) .related({ @@ -44,9 +40,7 @@ export class AuthRelationshipFilter extends RelationshipFilter { const predicate = this.createRelationshipOperation(pattern, nestedContext); - if (!predicate) return undefined; - - return this.wrapInNotIfNeeded(predicate); + return predicate; } protected createRelationshipOperation( @@ -55,15 +49,11 @@ export class AuthRelationshipFilter extends RelationshipFilter { ): Cypher.Predicate | undefined { const predicates = this.targetNodeFilters.map((c) => c.getPredicate(queryASTContext)); const innerPredicate = Cypher.and(...predicates); - if (!innerPredicate) return undefined; - const useExist = queryASTContext.neo4jGraphQLContext.neo4jDatabaseInfo?.gte("5.0"); + if (!innerPredicate) { + return; + } switch (this.operator) { case "ALL": { - if (!useExist) { - const patternComprehension = new Cypher.PatternComprehension(pattern).map(new Cypher.Literal(1)); - const sizeFunction = Cypher.size(patternComprehension.where(Cypher.not(innerPredicate))); - return Cypher.eq(sizeFunction, new Cypher.Literal(0)); - } const match = new Cypher.Match(pattern).where(innerPredicate); const negativeMatch = new Cypher.Match(pattern).where(Cypher.not(innerPredicate)); // Testing "ALL" requires testing that at least one element exists and that no elements not matching the filter exists @@ -78,18 +68,6 @@ export class AuthRelationshipFilter extends RelationshipFilter { } case "NONE": case "SOME": { - if (!this.relationship.isList && this.relationship.isNullable) { - return this.getSingleRelationshipOperation({ - pattern, - queryASTContext, - innerPredicate, - }); - } - if (!useExist) { - const patternComprehension = new Cypher.PatternComprehension(pattern).map(new Cypher.Literal(1)); - const sizeFunction = Cypher.size(patternComprehension.where(innerPredicate)); - return Cypher.gt(sizeFunction, new Cypher.Literal(0)); - } const matchClause = new Cypher.Match(pattern).where(innerPredicate); const existsPredicate = new Cypher.Exists(matchClause); return existsPredicate; diff --git a/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/JWTFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/JWTFilter.ts index c7ef5992ef..9d7cc251fe 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/JWTFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/authorization-filters/JWTFilter.ts @@ -18,61 +18,46 @@ */ import type { Predicate } from "@neo4j/cypher-builder"; -import type { QueryASTContext } from "../../QueryASTContext"; -import type { FilterOperator } from "../Filter"; -import { Filter } from "../Filter"; import Cypher from "@neo4j/cypher-builder"; import { createComparisonOperation } from "../../../utils/create-comparison-operator"; +import type { QueryASTContext } from "../../QueryASTContext"; import type { QueryASTNode } from "../../QueryASTNode"; +import type { FilterOperator } from "../Filter"; +import { Filter } from "../Filter"; export class JWTFilter extends Filter { protected operator: FilterOperator; protected JWTClaim: Cypher.Property; protected comparisonValue: unknown; - protected isNot: boolean; constructor({ operator, JWTClaim, comparisonValue, - isNot, }: { operator: FilterOperator; JWTClaim: Cypher.Property; comparisonValue: unknown; - isNot: boolean; }) { super(); this.operator = operator; this.JWTClaim = JWTClaim; this.comparisonValue = comparisonValue; - this.isNot = isNot; } public getChildren(): QueryASTNode[] { return []; } - + public print(): string { + return `${super.print()} <${this.operator} ${this.comparisonValue}>`; + } public getPredicate(_context: QueryASTContext): Predicate | undefined { - const operation = createComparisonOperation({ + const predicate = createComparisonOperation({ operator: this.operator, property: this.JWTClaim, param: new Cypher.Param(this.comparisonValue), }); - const predicate = this.wrapInNotIfNeeded(operation); return Cypher.and(Cypher.isNotNull(this.JWTClaim), predicate); } - - public print(): string { - return `${super.print()} <${this.operator} ${this.comparisonValue}>`; - } - - private wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - if (this.isNot) { - return Cypher.not(predicate); - } else { - return predicate; - } - } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts index 48f976b538..55d740d198 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts @@ -26,8 +26,10 @@ import type { CustomCypherSelection } from "../../selection/CustomCypherSelectio import type { FilterOperator } from "../Filter"; import { Filter } from "../Filter"; import { coalesceValueIfNeeded } from "../utils/coalesce-if-needed"; +import { createDateTimeOperation } from "../utils/create-date-time-operation"; import { createDurationOperation } from "../utils/create-duration-operation"; import { createPointOperation } from "../utils/create-point-operation"; +import { createTimeOperation } from "../utils/create-time-operation"; /** A property which comparison has already been parsed into a Param */ export class CypherFilter extends Filter { @@ -127,6 +129,24 @@ export class CypherFilter extends Filter { }); } + if (this.attribute.typeHelper.isDateTime()) { + return createDateTimeOperation({ + operator, + property: coalesceProperty, + param: this.comparisonValue, + attribute: this.attribute, + }); + } + + if (this.attribute.typeHelper.isTime()) { + return createTimeOperation({ + operator, + property: coalesceProperty, + param: this.comparisonValue, + attribute: this.attribute, + }); + } + return createComparisonOperation({ operator, property: coalesceProperty, param }); } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts new file mode 100644 index 0000000000..0eb4c1d65c --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import { createDateTimeOperation } from "../utils/create-date-time-operation"; +import { PropertyFilter } from "./PropertyFilter"; + +export class DateTimeFilter extends PropertyFilter { + protected getOperation(prop: Cypher.Property): Cypher.ComparisonOp { + return createDateTimeOperation({ + operator: this.operator || "EQ", + property: prop, + param: new Cypher.Param(this.comparisonValue), + attribute: this.attribute, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/ParamPropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/ParamPropertyFilter.ts index d641b4df22..4df64281fd 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/ParamPropertyFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/ParamPropertyFilter.ts @@ -18,11 +18,11 @@ */ import Cypher from "@neo4j/cypher-builder"; -import type { QueryASTContext } from "../../QueryASTContext"; -import { PropertyFilter } from "./PropertyFilter"; import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; -import type { FilterOperator } from "../Filter"; import type { RelationshipAdapter } from "../../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; +import type { QueryASTContext } from "../../QueryASTContext"; +import type { FilterOperator } from "../Filter"; +import { PropertyFilter } from "./PropertyFilter"; type CypherVariable = Cypher.Variable | Cypher.Property | Cypher.Param; @@ -33,8 +33,7 @@ export class ParamPropertyFilter extends PropertyFilter { constructor(options: { attribute: AttributeAdapter; comparisonValue: CypherVariable; - operator: FilterOperator; - isNot: boolean; + operator: FilterOperator, attachedTo?: "node" | "relationship"; relationship?: RelationshipAdapter; }) { diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/PropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/PropertyFilter.ts index 8e4d5d5995..3f3cea8841 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/PropertyFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/PropertyFilter.ts @@ -21,7 +21,6 @@ import Cypher from "@neo4j/cypher-builder"; import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; import { InterfaceEntityAdapter } from "../../../../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import type { RelationshipAdapter } from "../../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import { hasTarget } from "../../../utils/context-has-target"; import { createComparisonOperation } from "../../../utils/create-comparison-operator"; import type { QueryASTContext } from "../../QueryASTContext"; import type { QueryASTNode } from "../../QueryASTNode"; @@ -34,7 +33,6 @@ export class PropertyFilter extends Filter { protected relationship: RelationshipAdapter | undefined; protected comparisonValue: unknown; protected operator: FilterOperator; - protected isNot: boolean; // _NOT is deprecated protected attachedTo: "node" | "relationship"; constructor({ @@ -42,14 +40,12 @@ export class PropertyFilter extends Filter { relationship, comparisonValue, operator, - isNot, attachedTo, }: { attribute: AttributeAdapter; relationship?: RelationshipAdapter; comparisonValue: unknown; operator: FilterOperator; - isNot: boolean; attachedTo?: "node" | "relationship"; }) { super(); @@ -57,7 +53,6 @@ export class PropertyFilter extends Filter { this.relationship = relationship; this.comparisonValue = comparisonValue; this.operator = operator; - this.isNot = isNot; this.attachedTo = attachedTo ?? "node"; } @@ -66,19 +61,17 @@ export class PropertyFilter extends Filter { } public print(): string { - return `${super.print()} [${this.attribute.name}] <${this.isNot ? "NOT " : ""}${this.operator}>`; + return `${super.print()} [${this.attribute.name}] <${this.operator}>`; } public getPredicate(queryASTContext: QueryASTContext): Cypher.Predicate { const prop = this.getPropertyRefOrAliasesCase(queryASTContext); if (this.comparisonValue === null) { - return this.getNullPredicate(prop); + return Cypher.isNull(prop); } - const baseOperation = this.getOperation(prop); - - return this.wrapInNotIfNeeded(baseOperation); + return this.getOperation(prop); } private getPropertyRefOrAliasesCase(queryASTContext: QueryASTContext): Cypher.Property | Cypher.Case { @@ -106,7 +99,9 @@ export class PropertyFilter extends Filter { queryASTContext: QueryASTContext, concreteLabelsToAttributeAlias: [string[], string][] ): Cypher.Case { - if (!hasTarget(queryASTContext)) throw new Error("No parent node found!"); + if (!queryASTContext.hasTarget()) { + throw new Error("No parent node found!"); + } const aliasesCase = new Cypher.Case(); for (const [labels, databaseName] of concreteLabelsToAttributeAlias) { aliasesCase @@ -119,7 +114,9 @@ export class PropertyFilter extends Filter { private getPropertyRef(queryASTContext: QueryASTContext): Cypher.Property { if (this.attachedTo === "node") { - if (!hasTarget(queryASTContext)) throw new Error("No parent node found!"); + if (!queryASTContext.hasTarget()) { + throw new Error("No parent node found!"); + } return queryASTContext.target.property(this.attribute.databaseName); } else if (this.attachedTo === "relationship" && queryASTContext.relationship) { return queryASTContext.relationship.property(this.attribute.databaseName); @@ -153,17 +150,4 @@ export class PropertyFilter extends Filter { return createComparisonOperation({ operator, property: coalesceProperty, param }); } - - private getNullPredicate(propertyRef: Cypher.Property | Cypher.Case): Cypher.Predicate { - if (this.isNot) { - return Cypher.isNotNull(propertyRef); - } else { - return Cypher.isNull(propertyRef); - } - } - - private wrapInNotIfNeeded(predicate: Cypher.Predicate): Cypher.Predicate { - if (this.isNot) return Cypher.not(predicate); - else return predicate; - } } diff --git a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts similarity index 56% rename from packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.ts rename to packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts index a180e40050..dbd48d96f7 100644 --- a/packages/graphql/src/schema-model/parser/annotations-parser/unique-annotation.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts @@ -16,15 +16,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { DirectiveNode } from "graphql"; -import { uniqueDirective } from "../../../graphql/directives"; -import { UniqueAnnotation } from "../../annotation/UniqueAnnotation"; -import { parseArguments } from "../parse-arguments"; -export function parseUniqueAnnotation(directive: DirectiveNode): UniqueAnnotation { - const { constraintName } = parseArguments<{ constraintName?: string }>(uniqueDirective, directive); +import Cypher from "@neo4j/cypher-builder"; +import { createTimeOperation } from "../utils/create-time-operation"; +import { PropertyFilter } from "./PropertyFilter"; - return new UniqueAnnotation({ - constraintName, - }); +export class TimeFilter extends PropertyFilter { + protected getOperation(prop: Cypher.Property): Cypher.ComparisonOp { + return createTimeOperation({ + operator: this.operator || "EQ", + property: prop, + param: new Cypher.Param(this.comparisonValue), + attribute: this.attribute, + }); + } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts new file mode 100644 index 0000000000..338684781f --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; +import type { FilterOperator } from "../Filter"; + +export function createDateTimeOperation({ + operator, + property, + param, + attribute, +}: { + operator: FilterOperator; + property: Cypher.Expr; + param: Cypher.Param | Cypher.Variable | Cypher.Property; + attribute: AttributeAdapter; +}): Cypher.ComparisonOp { + const datetime = Cypher.datetime(param); + + switch (operator) { + case "LT": + return Cypher.lt(property, datetime); + case "LTE": + return Cypher.lte(property, datetime); + case "GT": + return Cypher.gt(property, datetime); + case "GTE": + return Cypher.gte(property, datetime); + case "EQ": { + if (attribute.typeHelper.isList()) { + const dateTimeList = createDateTimeListComprehension(param); + return Cypher.eq(property, dateTimeList); + } + + return Cypher.eq(property, datetime); + } + case "IN": { + const dateTimeList = createDateTimeListComprehension(param); + return Cypher.in(property, dateTimeList); + } + case "INCLUDES": + return Cypher.in(datetime, property); + default: + throw new Error(`Invalid operator ${operator}`); + } +} + +function createDateTimeListComprehension( + param: Cypher.Param | Cypher.Variable | Cypher.Property +): Cypher.ListComprehension { + const comprehensionVar = new Cypher.Variable(); + const mapDateTime = Cypher.datetime(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, param).map(mapDateTime); +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts new file mode 100644 index 0000000000..ba811a4b85 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; +import type { FilterOperator } from "../Filter"; + +export function createTimeOperation({ + operator, + property, + param, + attribute, +}: { + operator: FilterOperator; + property: Cypher.Expr; + param: Cypher.Param | Cypher.Variable | Cypher.Property; + attribute: AttributeAdapter; +}): Cypher.ComparisonOp { + const time = Cypher.time(param); + + switch (operator) { + case "LT": + return Cypher.lt(property, time); + case "LTE": + return Cypher.lte(property, time); + case "GT": + return Cypher.gt(property, time); + case "GTE": + return Cypher.gte(property, time); + case "EQ": { + if (attribute.typeHelper.isList()) { + const timeList = createTimeListComprehension(param); + return Cypher.eq(property, timeList); + } + + return Cypher.eq(property, time); + } + case "IN": { + const timeList = createTimeListComprehension(param); + return Cypher.in(property, timeList); + } + case "INCLUDES": + return Cypher.in(time, property); + default: + throw new Error(`Invalid operator ${operator}`); + } +} + +function createTimeListComprehension( + param: Cypher.Param | Cypher.Variable | Cypher.Property +): Cypher.ListComprehension { + const comprehensionVar = new Cypher.Variable(); + const mapTime = Cypher.time(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, param).map(mapTime); +} diff --git a/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts b/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts index a70c9d4a2e..2a40ca2518 100644 --- a/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts +++ b/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts @@ -78,6 +78,25 @@ export class PropertyInputField extends InputField { const mapPoint = Cypher.point(comprehensionVar); return new Cypher.ListComprehension(comprehensionVar, variable).map(mapPoint); } + + if (this.attribute.typeHelper.isDateTime()) { + if (!this.attribute.typeHelper.isList()) { + return Cypher.datetime(variable); + } + const comprehensionVar = new Cypher.Variable(); + const mapDateTime = Cypher.datetime(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, variable).map(mapDateTime); + } + + if (this.attribute.typeHelper.isTime()) { + if (!this.attribute.typeHelper.isList()) { + return Cypher.time(variable); + } + const comprehensionVar = new Cypher.Variable(); + const mapTime = Cypher.time(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, variable).map(mapTime); + } + return variable; } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts index 0689e763b7..6bffecd7b3 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/AggregationOperation.ts @@ -152,7 +152,7 @@ export class AggregationOperation extends Operation { if (this.entity instanceof RelationshipAdapter) { const relVar = new Cypher.Relationship(); const targetNode = new Cypher.Node(); - const relDirection = this.entity.getCypherDirection(this.directed); + const relDirection = this.entity.getCypherDirection(); return parentContext.push({ relationship: relVar, target: targetNode, direction: relDirection }); } else { const targetNode = new Cypher.Node(); diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts index bea14359aa..b78ec02c11 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ConnectionReadOperation.ts @@ -32,7 +32,7 @@ import type { Pagination } from "../pagination/Pagination"; import type { EntitySelection } from "../selection/EntitySelection"; import { CypherPropertySort } from "../sort/CypherPropertySort"; import type { Sort, SortField } from "../sort/Sort"; -import { CypherScalarOperation } from "./CypherScalarOperation"; +import { CypherAttributeOperation } from "./CypherAttributeOperation"; import type { OperationTranspileResult } from "./operations"; import { Operation } from "./operations"; @@ -399,7 +399,7 @@ export class ConnectionReadOperation extends Operation { if ( nodeField instanceof OperationField && nodeField.isCypherField() && - nodeField.operation instanceof CypherScalarOperation + nodeField.operation instanceof CypherAttributeOperation ) { const cypherFieldName = nodeField.operation.cypherAttributeField.name; if (cypherSortFieldsFlagMap[cypherFieldName]) { diff --git a/packages/graphql/src/translate/queryAST/ast/operations/CypherScalarOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/CypherAttributeOperation.ts similarity index 97% rename from packages/graphql/src/translate/queryAST/ast/operations/CypherScalarOperation.ts rename to packages/graphql/src/translate/queryAST/ast/operations/CypherAttributeOperation.ts index 922a0b3cb0..540343e4aa 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/CypherScalarOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/CypherAttributeOperation.ts @@ -27,7 +27,7 @@ import { Operation, type OperationTranspileResult } from "./operations"; /** * This operation is used to return top-level and nested @cypher fields that returns a scalar value. **/ -export class CypherScalarOperation extends Operation { +export class CypherAttributeOperation extends Operation { private selection: EntitySelection; public cypherAttributeField: AttributeAdapter; private isNested: boolean; diff --git a/packages/graphql/src/translate/queryAST/ast/operations/CypherOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/CypherEntityOperation.ts similarity index 98% rename from packages/graphql/src/translate/queryAST/ast/operations/CypherOperation.ts rename to packages/graphql/src/translate/queryAST/ast/operations/CypherEntityOperation.ts index afc55465fb..9bb2d65d69 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/CypherOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/CypherEntityOperation.ts @@ -26,7 +26,7 @@ import type { EntitySelection, SelectionClause } from "../selection/EntitySelect import { ReadOperation } from "./ReadOperation"; import type { OperationTranspileResult } from "./operations"; -export class CypherOperation extends ReadOperation { +export class CypherEntityOperation extends ReadOperation { private cypherAttributeField: AttributeAdapter; constructor({ diff --git a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts index 14b04610c9..bdd8f4fef4 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/FulltextOperation.ts @@ -18,6 +18,7 @@ */ import Cypher from "@neo4j/cypher-builder"; +import type { FulltextField } from "../../../../schema-model/annotation/FulltextAnnotation"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter"; import { filterTruthy } from "../../../../utils/utils"; @@ -25,16 +26,16 @@ import type { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; import type { ScoreField } from "../fields/ScoreField"; import type { EntitySelection } from "../selection/EntitySelection"; -import { ReadOperation } from "./ReadOperation"; -import type { OperationTranspileResult } from "./operations"; +import { ScoreSort } from "../sort/ScoreSort"; +import { ConnectionReadOperation } from "./ConnectionReadOperation"; export type FulltextOptions = { - index: string; + index: FulltextField; phrase: string; score: Cypher.Variable; }; -export class FulltextOperation extends ReadOperation { +export class FulltextOperation extends ConnectionReadOperation { private scoreField: ScoreField | undefined; constructor({ @@ -57,37 +58,62 @@ export class FulltextOperation extends ReadOperation { this.scoreField = scoreField; } - public transpile(context: QueryASTContext): OperationTranspileResult { - const { clauses, projectionExpr } = super.transpile(context); - - const extraProjectionColumns: Array<[Cypher.Expr, Cypher.Variable]> = []; + public getChildren(): QueryASTNode[] { + return filterTruthy([...super.getChildren(), this.scoreField]); + } - if (this.scoreField) { - const scoreProjection = this.scoreField.getProjectionField(); + protected createProjectionMapForEdge(context: QueryASTContext): Cypher.Map { + const edgeProjectionMap = new Cypher.Map(); - extraProjectionColumns.push([scoreProjection.score, new Cypher.NamedVariable("score")]); + edgeProjectionMap.set("node", this.createProjectionMapForNode(context)); + if (this.scoreField && context.neo4jGraphQLContext.fulltext) { + edgeProjectionMap.set("score", context.neo4jGraphQLContext.fulltext.scoreVariable); } - - return { - clauses, - projectionExpr, - extraProjectionColumns, - }; + return edgeProjectionMap; } - public getChildren(): QueryASTNode[] { - return filterTruthy([...super.getChildren(), this.scoreField]); + protected getUnwindClause( + context: QueryASTContext, + edgeVar: Cypher.Variable, + edgesVar: Cypher.Variable + ): Cypher.With { + if ((this.scoreField || this.hasScoreSort()) && context.neo4jGraphQLContext.fulltext) { + // No relationship, so we directly unwind node and score + return new Cypher.Unwind([edgesVar, edgeVar]).with( + [edgeVar.property("node"), context.target], + [edgeVar.property("score"), context.neo4jGraphQLContext.fulltext.scoreVariable] + ); + } else { + return super.getUnwindClause(context, edgeVar, edgesVar); + } } - protected getReturnStatement(context: QueryASTContext, returnVariable: Cypher.Variable): Cypher.Return { - const returnClause = super.getReturnStatement(context, returnVariable); + protected getWithCollectEdgesAndTotalCount( + nestedContext: QueryASTContext, + edgesVar: Cypher.Variable, + totalCount: Cypher.Variable + ): Cypher.With { + if ((this.scoreField || this.hasScoreSort()) && nestedContext.neo4jGraphQLContext.fulltext) { + const nodeAndRelationshipMap = new Cypher.Map({ + node: nestedContext.target, + }); - if (this.scoreField) { - const scoreProjection = this.scoreField.getProjectionField(); + if (nestedContext.relationship) { + nodeAndRelationshipMap.set("relationship", nestedContext.relationship); + } - returnClause.addColumns([scoreProjection.score, "score"]); + nodeAndRelationshipMap.set("score", nestedContext.neo4jGraphQLContext.fulltext.scoreVariable); + + return new Cypher.With([Cypher.collect(nodeAndRelationshipMap), edgesVar]).with(edgesVar, [ + Cypher.size(edgesVar), + totalCount, + ]); + } else { + return super.getWithCollectEdgesAndTotalCount(nestedContext, edgesVar, totalCount); } + } - return returnClause; + private hasScoreSort(): boolean { + return this.sortFields.some(({ node }) => node.some((sort) => sort instanceof ScoreSort)); } } diff --git a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts index d6e8526604..3c8bb62a21 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/ReadOperation.ts @@ -92,20 +92,13 @@ export class ReadOperation extends Operation { return filterTruthy(this.authFilters.map((f) => f.getPredicate(context))); } - protected getProjectionClause( - context: QueryASTContext, - returnVariable: Cypher.Variable, - isArray: boolean - ): Cypher.Return { + protected getProjectionClause(context: QueryASTContext, returnVariable: Cypher.Variable): Cypher.Return { if (!hasTarget(context)) { throw new Error("No parent node found!"); } const projection = this.getProjectionMap(context); - let aggregationExpr: Cypher.Expr = Cypher.collect(context.target); - if (!isArray) { - aggregationExpr = Cypher.head(aggregationExpr); - } + const aggregationExpr = Cypher.collect(context.target); const withClause = new Cypher.With([projection, context.target]); if (this.sortFields.length > 0 || this.pagination) { @@ -148,7 +141,7 @@ export class ReadOperation extends Operation { const authFiltersPredicate = this.getAuthFilterPredicate(nestedContext); const ret: Cypher.Return = this.relationship - ? this.getProjectionClause(nestedContext, context.returnVariable, this.relationship.isList) + ? this.getProjectionClause(nestedContext, context.returnVariable) : this.getReturnStatement( isCreateSelection || isUpdateSelection ? context : nestedContext, nestedContext.returnVariable @@ -215,6 +208,11 @@ export class ReadOperation extends Operation { } matchBlock.push(...extraMatches, extraMatchesWith); + if (this.relationship) { + const distinctTargetWith = new Cypher.With(nestedContext.target).distinct(); + matchBlock.push(distinctTargetWith); + } + clause = Cypher.utils.concat( ...matchBlock, ...authFilterSubqueries, diff --git a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts index c11663534d..ed9767502b 100644 --- a/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts +++ b/packages/graphql/src/translate/queryAST/ast/operations/composite/CompositeAggregationPartial.ts @@ -78,7 +78,7 @@ export class CompositeAggregationPartial extends QueryASTNode { if (this.entity instanceof RelationshipAdapter) { const relVar = new Cypher.Relationship(); - const relDirection = this.entity.getCypherDirection(this.directed); + const relDirection = this.entity.getCypherDirection(); if (this.attachedTo === "relationship") { target = relVar; } diff --git a/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts index ac604c6954..111bdc0917 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/FulltextSelection.ts @@ -26,32 +26,31 @@ import { EntitySelection, type SelectionClause } from "./EntitySelection"; export class FulltextSelection extends EntitySelection { private target: ConcreteEntityAdapter; - private fulltext: FulltextOptions; + private fulltextOptions: FulltextOptions; private scoreVariable: Cypher.Variable; constructor({ target, - fulltext, + fulltextOptions, scoreVariable, }: { target: ConcreteEntityAdapter; - fulltext: FulltextOptions; + fulltextOptions: FulltextOptions; scoreVariable: Cypher.Variable; }) { super(); this.target = target; - this.fulltext = fulltext; + this.fulltextOptions = fulltextOptions; this.scoreVariable = scoreVariable; } - public apply(context: QueryASTContext): { nestedContext: QueryASTContext; selection: SelectionClause; } { const node = new Cypher.Node(); - const phraseParam = new Cypher.Param(this.fulltext.phrase); - const indexName = new Cypher.Literal(this.fulltext.index); + const phraseParam = new Cypher.Param(this.fulltextOptions.phrase); + const indexName = new Cypher.Literal(this.fulltextOptions.index.indexName); const fulltextClause: Cypher.Yield = Cypher.db.index.fulltext .queryNodes(indexName, phraseParam) diff --git a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts index d305db5497..2aa656d0d4 100644 --- a/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts +++ b/packages/graphql/src/translate/queryAST/ast/selection/RelationshipSelection.ts @@ -26,17 +26,15 @@ import type { QueryASTContext } from "../QueryASTContext"; import { EntitySelection, type SelectionClause } from "./EntitySelection"; export class RelationshipSelection extends EntitySelection { - protected relationship: RelationshipAdapter; + private relationship: RelationshipAdapter; // Overrides relationship target for composite entities private targetOverride: ConcreteEntityAdapter | undefined; private alias: string | undefined; - private directed?: boolean; private optional: boolean; constructor({ relationship, alias, - directed, targetOverride, optional, }: { @@ -49,7 +47,6 @@ export class RelationshipSelection extends EntitySelection { super(); this.relationship = relationship; this.alias = alias; - this.directed = directed; this.targetOverride = targetOverride; this.optional = optional ?? false; } @@ -64,7 +61,7 @@ export class RelationshipSelection extends EntitySelection { const relationshipTarget = this.targetOverride ?? this.relationship.target; const targetNode = createNode(this.alias); const labels = getEntityLabels(relationshipTarget, context.neo4jGraphQLContext); - const relDirection = this.getRelationshipDirection(); + const relDirection = this.relationship.getCypherDirection(); const pattern = new Cypher.Pattern(context.target) .related(relVar, { direction: relDirection, type: this.relationship.type }) @@ -81,15 +78,4 @@ export class RelationshipSelection extends EntitySelection { selection: match, }; } - - protected getRelationshipDirection(): "left" | "right" | "undirected" { - return this.relationship.getCypherDirection(this.directed); - } -} - -/** Enforces direction on the relationship selection, regardless of direction configuration **/ -export class DirectedRelationshipSelection extends RelationshipSelection { - protected getRelationshipDirection(): "left" | "right" { - return this.relationship.cypherDirectionFromRelDirection(); - } } diff --git a/packages/graphql/src/translate/queryAST/ast/sort/CypherPropertySort.ts b/packages/graphql/src/translate/queryAST/ast/sort/CypherPropertySort.ts index d66afc7a4d..112e44edb5 100644 --- a/packages/graphql/src/translate/queryAST/ast/sort/CypherPropertySort.ts +++ b/packages/graphql/src/translate/queryAST/ast/sort/CypherPropertySort.ts @@ -21,14 +21,14 @@ import Cypher from "@neo4j/cypher-builder"; import type { AttributeAdapter } from "../../../../schema-model/attribute/model-adapters/AttributeAdapter"; import type { QueryASTContext } from "../QueryASTContext"; import type { QueryASTNode } from "../QueryASTNode"; -import type { CypherScalarOperation } from "../operations/CypherScalarOperation"; +import type { CypherAttributeOperation } from "../operations/CypherAttributeOperation"; import type { SortField } from "./Sort"; import { Sort } from "./Sort"; export class CypherPropertySort extends Sort { private attribute: AttributeAdapter; private direction: Cypher.Order; - private cypherOperation: CypherScalarOperation; + private cypherOperation: CypherAttributeOperation; constructor({ attribute, @@ -37,7 +37,7 @@ export class CypherPropertySort extends Sort { }: { attribute: AttributeAdapter; direction: Cypher.Order; - cypherOperation: CypherScalarOperation; + cypherOperation: CypherAttributeOperation; }) { super(); this.attribute = attribute; diff --git a/packages/graphql/src/translate/queryAST/factory/AuthFilterFactory.ts b/packages/graphql/src/translate/queryAST/factory/AuthFilterFactory.ts index c365aed93b..a376d13764 100644 --- a/packages/graphql/src/translate/queryAST/factory/AuthFilterFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/AuthFilterFactory.ts @@ -29,8 +29,9 @@ import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphq import { asArray } from "../../../utils/utils"; import { isLogicalOperator } from "../../utils/logical-operators"; import type { ConnectionFilter } from "../ast/filters/ConnectionFilter"; + import type { Filter, FilterOperator, RelationshipWhereOperator } from "../ast/filters/Filter"; -import { isRelationshipOperator } from "../ast/filters/Filter"; +import { isLegacyRelationshipOperator } from "../ast/filters/Filter"; import { LogicalFilter } from "../ast/filters/LogicalFilter"; import type { RelationshipFilter } from "../ast/filters/RelationshipFilter"; import { AuthConnectionFilter } from "../ast/filters/authorization-filters/AuthConnectionFilter"; @@ -90,7 +91,7 @@ export class AuthFilterFactory extends FilterFactory { where: GraphQLWhereArg, context: Neo4jGraphQLTranslationContext ): Filter[] { - return Object.entries(where).map(([key, value]) => { + return Object.entries(where).flatMap(([key, value]) => { if (isLogicalOperator(key)) { const nestedFilters = asArray(value).flatMap((v) => { return this.createJWTFilters(jwtPayload, v, context); @@ -101,7 +102,7 @@ export class AuthFilterFactory extends FilterFactory { filters: nestedFilters, }); } - const { fieldName, operator, isNot } = parseWhereField(key); + const { fieldName, operator } = parseWhereField(key); if (!fieldName) { throw new Error(`Failed to find field name in filter: ${key}`); } @@ -118,12 +119,24 @@ export class AuthFilterFactory extends FilterFactory { target = jwtPayload.property(...paths); } + if (!operator) { + return this.wrapMultipleFiltersInLogical(this.getGenericJWTFilters(value, target)); + } + return new JWTFilter({ + operator: operator, + JWTClaim: target, + comparisonValue: value, + }); + }); + } + private getGenericJWTFilters(genericOperator: Record, target: Cypher.Property): JWTFilter[] { + return Object.entries(genericOperator).map(([key, value]): JWTFilter => { + const operator = this.parseGenericOperator(key); return new JWTFilter({ - operator: operator || "EQ", + operator, JWTClaim: target, comparisonValue: value, - isNot, }); }); } @@ -132,19 +145,15 @@ export class AuthFilterFactory extends FilterFactory { attribute, comparisonValue, operator, - isNot, attachedTo, relationship, }: { attribute: AttributeAdapter; comparisonValue: unknown; operator: FilterOperator | undefined; - isNot: boolean; attachedTo?: "node" | "relationship"; relationship?: RelationshipAdapter; }): Filter { - const filterOperator = operator || "EQ"; - const isCypherVariable = comparisonValue instanceof Cypher.Variable || comparisonValue instanceof Cypher.Property || @@ -160,9 +169,26 @@ export class AuthFilterFactory extends FilterFactory { if (attribute.annotations.cypher?.targetEntity) { const entityAdapter = getEntityAdapter(attribute.annotations.cypher.targetEntity); - if (operator && !isRelationshipOperator(operator)) { + if (operator && !isLegacyRelationshipOperator(operator)) { throw new Error(`Invalid operator ${operator} for relationship`); } + // path for generic filters input, in v8 it will be the only path + if (!operator && attribute.typeHelper.isList()) { + const genericFilters = Object.entries(comparisonValue as any).flatMap(([quantifier, predicate]) => { + const legacyOperator = this.convertRelationshipOperatorToLegacyOperator(quantifier); + return this.createCypherRelationshipFilter({ + where: predicate as any, + selection, + target: entityAdapter, + operator: legacyOperator, + attribute, + }); + }); + return new LogicalFilter({ + operation: "AND", + filters: genericFilters, + }); + } return new LogicalFilter({ operation: "AND", @@ -170,10 +196,7 @@ export class AuthFilterFactory extends FilterFactory { where: comparisonValue as GraphQLWhereArg, selection, target: entityAdapter, - filterOps: { - isNot, - operator, - }, + operator, attribute, }), }); @@ -184,7 +207,7 @@ export class AuthFilterFactory extends FilterFactory { selection, attribute, comparisonValue: comparisonValue, - operator: filterOperator, + operator: operator ?? "EQ", checkIsNotNull: true, }); } @@ -195,18 +218,19 @@ export class AuthFilterFactory extends FilterFactory { selection, attribute, comparisonValue: comparisonValueParam, - operator: filterOperator, + operator: operator ?? "EQ", }); } - + if (!operator) { + throw new Error(`Operator is required for property filter`); + } // This is probably not needed, but avoid changing the cypher if (typeof comparisonValue === "boolean") { return new ParamPropertyFilter({ attribute, relationship, comparisonValue: new Cypher.Param(comparisonValue), - isNot, - operator: filterOperator, + operator, attachedTo, }); } @@ -216,8 +240,7 @@ export class AuthFilterFactory extends FilterFactory { attribute, relationship, comparisonValue: comparisonValue, - isNot, - operator: filterOperator, + operator, attachedTo, }); } else { @@ -226,8 +249,7 @@ export class AuthFilterFactory extends FilterFactory { attribute, relationship, comparisonValue: comparisonValue, - isNot, - operator: filterOperator, + operator, attachedTo, }); } @@ -235,8 +257,7 @@ export class AuthFilterFactory extends FilterFactory { attribute, relationship, comparisonValue: new Cypher.Param(comparisonValue), - isNot, - operator: filterOperator, + operator, attachedTo, }); } @@ -245,7 +266,6 @@ export class AuthFilterFactory extends FilterFactory { protected createRelationshipFilterTreeNode(options: { relationship: RelationshipAdapter; target: ConcreteEntityAdapter | InterfaceEntityAdapter; - isNot: boolean; operator: RelationshipWhereOperator; }): RelationshipFilter { return new AuthRelationshipFilter(options); @@ -254,8 +274,7 @@ export class AuthFilterFactory extends FilterFactory { protected createConnectionFilterTreeNode(options: { relationship: RelationshipAdapter; target: ConcreteEntityAdapter | InterfaceEntityAdapter; - isNot: boolean; - operator: RelationshipWhereOperator | undefined; + operator: RelationshipWhereOperator; }): ConnectionFilter { return new AuthConnectionFilter(options); } diff --git a/packages/graphql/src/translate/queryAST/factory/FieldFactory.ts b/packages/graphql/src/translate/queryAST/factory/FieldFactory.ts index d14a9cf8c9..fed27d7395 100644 --- a/packages/graphql/src/translate/queryAST/factory/FieldFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/FieldFactory.ts @@ -257,9 +257,6 @@ export class FieldFactory { if (rawFields) { if (attribute.typeHelper.isObject()) { const concreteEntity = this.queryASTFactory.schemaModel.getConcreteEntityAdapter(typeName); - if (!concreteEntity) { - throw new Error(`Entity ${typeName} not found`); - } return this.createCypherOperationField({ target: concreteEntity, @@ -270,14 +267,8 @@ export class FieldFactory { }); } else if (attribute.typeHelper.isAbstract()) { const entity = this.queryASTFactory.schemaModel.getEntity(typeName); - // Raise an error as we expect that any complex attributes type are always entities - if (!entity) { - throw new Error(`Entity ${typeName} not found`); - } - if (!entity.isCompositeEntity()) { - throw new Error(`Entity ${typeName} is not a composite entity`); - } - const targetEntity = getEntityAdapter(entity); + + const targetEntity = entity ? getEntityAdapter(entity) : undefined; return this.createCypherOperationField({ target: targetEntity, field, diff --git a/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts b/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts index ac867b4f57..992371ec1d 100644 --- a/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts @@ -32,25 +32,35 @@ import { ConnectionFilter } from "../ast/filters/ConnectionFilter"; import { CypherOneToOneRelationshipFilter } from "../ast/filters/CypherOneToOneRelationshipFilter"; import { CypherRelationshipFilter } from "../ast/filters/CypherRelationshipFilter"; import type { Filter, FilterOperator, RelationshipWhereOperator } from "../ast/filters/Filter"; -import { isRelationshipOperator } from "../ast/filters/Filter"; +import { isLegacyRelationshipOperator } from "../ast/filters/Filter"; import { LogicalFilter } from "../ast/filters/LogicalFilter"; import { RelationshipFilter } from "../ast/filters/RelationshipFilter"; +import { AggregationDateTimeFilter } from "../ast/filters/aggregation/AggregationDateTimePropertyFilter"; import { AggregationDurationFilter } from "../ast/filters/aggregation/AggregationDurationPropertyFilter"; import { AggregationFilter } from "../ast/filters/aggregation/AggregationFilter"; import { AggregationPropertyFilter } from "../ast/filters/aggregation/AggregationPropertyFilter"; +import { AggregationTimeFilter } from "../ast/filters/aggregation/AggregationTimePropertyFilter"; import { CountFilter } from "../ast/filters/aggregation/CountFilter"; import { CypherFilter } from "../ast/filters/property-filters/CypherFilter"; +import { DateTimeFilter } from "../ast/filters/property-filters/DateTimeFilter"; import { DurationFilter } from "../ast/filters/property-filters/DurationFilter"; import { PointFilter } from "../ast/filters/property-filters/PointFilter"; import { PropertyFilter } from "../ast/filters/property-filters/PropertyFilter"; +import { TimeFilter } from "../ast/filters/property-filters/TimeFilter"; import { TypenameFilter } from "../ast/filters/property-filters/TypenameFilter"; import { CustomCypherSelection } from "../ast/selection/CustomCypherSelection"; import { getConcreteEntities } from "../utils/get-concrete-entities"; import { isConcreteEntity } from "../utils/is-concrete-entity"; import { isInterfaceEntity } from "../utils/is-interface-entity"; +import { isRelationshipEntity } from "../utils/is-relationship-entity"; import { isUnionEntity } from "../utils/is-union-entity"; import type { QueryASTFactory } from "./QueryASTFactory"; -import { parseAggregationWhereFields, parseConnectionWhereFields, parseWhereField } from "./parsers/parse-where-field"; +import { + parseAggregationWhereFields, + parseWhereField, + type AggregationLogicalOperator, + type AggregationOperator, +} from "./parsers/parse-where-field"; type AggregateWhereInput = { count: number; @@ -71,7 +81,8 @@ export class FilterFactory { private createConnectionFilter( relationship: RelationshipAdapter, where: ConnectionWhereArg, - filterOps: { isNot: boolean; operator: RelationshipWhereOperator | undefined } + + operator: RelationshipWhereOperator ): Filter[] { if ( isInterfaceEntity(relationship.target) && @@ -80,8 +91,7 @@ export class FilterFactory { const connectionFilter = this.createConnectionFilterTreeNode({ relationship: relationship, target: relationship.target, - isNot: filterOps.isNot, - operator: filterOps.operator, + operator, }); const filters = this.createConnectionPredicates({ rel: relationship, entity: relationship.target, where }); connectionFilter.addFilters(filters); @@ -99,8 +109,7 @@ export class FilterFactory { const connectionFilter = this.createConnectionFilterTreeNode({ relationship: relationship, target: concreteEntity, - isNot: filterOps.isNot, - operator: filterOps.operator, + operator, }); const filters = this.createConnectionPredicates({ @@ -112,7 +121,7 @@ export class FilterFactory { connectionFilter.addFilters(filters); connectionFilters.push(connectionFilter); } - const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(relationship.target, filterOps.operator); + const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(relationship.target, operator); return this.wrapMultipleFiltersInLogical(connectionFilters, logicalOp); } @@ -143,11 +152,11 @@ export class FilterFactory { ]; } - const connectionWhereField = parseConnectionWhereFields(key); - if (rel && connectionWhereField.fieldName === "edge") { + if (rel && key === "edge") { return this.createEdgeFilters(rel, value); } - if (connectionWhereField.fieldName === "node") { + + if (key === "node") { if (partialOf && isInterfaceEntity(partialOf) && isConcreteEntity(entity)) { return this.createInterfaceNodeFilters({ entity: partialOf, @@ -172,15 +181,11 @@ export class FilterFactory { attribute, comparisonValue, operator, - isNot, }: { attribute: AttributeAdapter; comparisonValue: GraphQLWhereArg; operator: FilterOperator | undefined; - isNot: boolean; }): Filter | Filter[] { - const filterOperator = operator || "EQ"; - const selection = new CustomCypherSelection({ operationField: attribute, rawArguments: {}, @@ -190,18 +195,29 @@ export class FilterFactory { if (attribute.annotations.cypher?.targetEntity) { const entityAdapter = getEntityAdapter(attribute.annotations.cypher.targetEntity); - if (operator && !isRelationshipOperator(operator)) { + if (operator && !isLegacyRelationshipOperator(operator)) { throw new Error(`Invalid operator ${operator} for relationship`); } + // path for generic filters input, in v8 it will be the only path + if (!operator && attribute.typeHelper.isList()) { + const genericFilters = Object.entries(comparisonValue).flatMap(([quantifier, predicate]) => { + const legacyOperator = this.convertRelationshipOperatorToLegacyOperator(quantifier); + return this.createCypherRelationshipFilter({ + where: predicate, + selection, + target: entityAdapter, + operator: legacyOperator, + attribute, + }); + }); + return this.wrapMultipleFiltersInLogical(genericFilters); + } return this.createCypherRelationshipFilter({ where: comparisonValue, selection, target: entityAdapter, - filterOps: { - isNot, - operator, - }, + operator: operator ?? "SOME", attribute, }); } @@ -212,7 +228,7 @@ export class FilterFactory { selection, attribute, comparisonValue: comparisonValueParam, - operator: filterOperator, + operator: operator ?? "EQ", }); } @@ -221,33 +237,30 @@ export class FilterFactory { relationship, comparisonValue, operator, - isNot, attachedTo, }: { attribute: AttributeAdapter; relationship?: RelationshipAdapter; comparisonValue: GraphQLWhereArg; operator: FilterOperator | undefined; - isNot: boolean; attachedTo?: "node" | "relationship"; }): Filter | Filter[] { - const filterOperator = operator || "EQ"; - if (attribute.annotations.cypher) { return this.createCypherFilter({ attribute, comparisonValue, operator, - isNot, }); } - + // Implicit _EQ filters are removed but the argument "operator" can still be undefined in some cases, for instance: + // Cypher 1:1 relationship filters as they are stored as Attribute. + // Federation Subgraph resolver, _entities field implementation is using the FilterFactory { "__typename": "Product", "upc": "abc123"}. + operator = operator ?? "EQ"; if (attribute.typeHelper.isDuration()) { return new DurationFilter({ attribute, comparisonValue, - isNot, - operator: filterOperator, + operator, attachedTo, }); } @@ -255,8 +268,23 @@ export class FilterFactory { return new PointFilter({ attribute, comparisonValue, - isNot, - operator: filterOperator, + operator, + attachedTo, + }); + } + if (attribute.typeHelper.isDateTime()) { + return new DateTimeFilter({ + attribute, + comparisonValue, + operator, + attachedTo, + }); + } + if (attribute.typeHelper.isTime()) { + return new TimeFilter({ + attribute, + comparisonValue, + operator, attachedTo, }); } @@ -265,8 +293,7 @@ export class FilterFactory { attribute, relationship, comparisonValue, - isNot, - operator: filterOperator, + operator, attachedTo, }); } @@ -274,7 +301,7 @@ export class FilterFactory { private createRelationshipFilter( relationship: RelationshipAdapter, where: GraphQLWhereArg, - filterOps: { isNot: boolean; operator: RelationshipWhereOperator | undefined } + operator: RelationshipWhereOperator | undefined ): Filter[] { /** * The logic below can be confusing, but it's to handle the following cases: @@ -285,8 +312,6 @@ export class FilterFactory { if (!isNull && Object.keys(where).length === 0) { return []; } - // this is because if isNull is true we want to wrap the Exist subclause in a NOT, but if isNull is true and isNot is true they negate each other - const isNot = isNull ? !filterOps.isNot : filterOps.isNot; const filteredEntities = getConcreteEntities(relationship.target, where); const relationshipFilters: RelationshipFilter[] = []; @@ -294,8 +319,7 @@ export class FilterFactory { const relationshipFilter = this.createRelationshipFilterTreeNode({ relationship, target: concreteEntity, - isNot, - operator: filterOps.operator || "SOME", + operator: operator ?? "SOME", }); if (!isNull) { @@ -306,7 +330,7 @@ export class FilterFactory { relationshipFilters.push(relationshipFilter); } - const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(relationship.target, filterOps.operator); + const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(relationship.target, operator); return this.wrapMultipleFiltersInLogical(relationshipFilters, logicalOp); } @@ -314,13 +338,13 @@ export class FilterFactory { selection, target, where, - filterOps, attribute, + operator, }: { selection: CustomCypherSelection; target: EntityAdapter; where: GraphQLWhereArg; - filterOps: { isNot: boolean; operator: RelationshipWhereOperator | undefined }; + operator: RelationshipWhereOperator | undefined; attribute: AttributeAdapter; }): Filter[] { /** @@ -332,7 +356,7 @@ export class FilterFactory { if (!isNull && Object.keys(where).length === 0) { return []; } - + // TODO the below logic is unnecessary, Cypher relationship are not supported for Composite Entities const filteredEntities = getConcreteEntities(target, where); const filters: (CypherOneToOneRelationshipFilter | CypherRelationshipFilter)[] = []; for (const concreteEntity of filteredEntities) { @@ -340,9 +364,8 @@ export class FilterFactory { const options = { selection, - isNot: filterOps.isNot, isNull, - operator: filterOps.operator || "SOME", + operator: operator ?? "SOME", attribute, returnVariable, }; @@ -359,7 +382,7 @@ export class FilterFactory { filters.push(filter); } - const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(target, filterOps.operator); + const logicalOp = this.getLogicalOperatorForRelatedNodeFilters(target, operator); return this.wrapMultipleFiltersInLogical(filters, logicalOp); } @@ -367,7 +390,6 @@ export class FilterFactory { protected createCypherOneToOneRelationshipFilterTreeNode(options: { selection: CustomCypherSelection; attribute: AttributeAdapter; - isNot: boolean; isNull: boolean; operator: RelationshipWhereOperator; returnVariable: Cypher.Node; @@ -379,7 +401,6 @@ export class FilterFactory { protected createCypherRelationshipFilterTreeNode(options: { selection: CustomCypherSelection; attribute: AttributeAdapter; - isNot: boolean; isNull: boolean; operator: RelationshipWhereOperator; returnVariable: Cypher.Node; @@ -391,7 +412,6 @@ export class FilterFactory { protected createRelationshipFilterTreeNode(options: { relationship: RelationshipAdapter; target: ConcreteEntityAdapter | InterfaceEntityAdapter; - isNot: boolean; operator: RelationshipWhereOperator; }): RelationshipFilter { return new RelationshipFilter(options); @@ -401,8 +421,7 @@ export class FilterFactory { protected createConnectionFilterTreeNode(options: { relationship: RelationshipAdapter; target: ConcreteEntityAdapter | InterfaceEntityAdapter; - isNot: boolean; - operator: RelationshipWhereOperator | undefined; + operator: RelationshipWhereOperator; }): ConnectionFilter { return new ConnectionFilter(options); } @@ -420,58 +439,9 @@ export class FilterFactory { }): Filter[] { const filters = filterTruthy( Object.entries(whereFields).flatMap(([key, value]): Filter | Filter[] | undefined => { - const valueAsArray = asArray(value); - if (isLogicalOperator(key)) { - const nestedFilters = valueAsArray.flatMap((nestedWhere) => { - return this.createInterfaceNodeFilters({ entity, targetEntity, whereFields: nestedWhere }); - }); - return new LogicalFilter({ - operation: key, - filters: nestedFilters, - }); - } - if (key === "typename_IN" || key === "typename") { - const acceptedEntities = entity.concreteEntities.filter((concreteEntity) => { - return valueAsArray.some((typenameFilterValue) => typenameFilterValue === concreteEntity.name); - }); - return new TypenameFilter(acceptedEntities); - } - - const { fieldName, operator, isNot, isConnection, isAggregate } = parseWhereField(key); - const relationshipDeclaration = entity.findRelationshipDeclarations(fieldName); - if (targetEntity && relationshipDeclaration) { - const relationship = relationshipDeclaration.relationshipImplementations.find( - (r) => r.source.name === targetEntity.name - ); - if (!relationship) { - throw new Error(`Relationship ${fieldName} not found`); - } - return this.createRelatedNodeFilters({ - relationship, - value, - operator, - isNot, - isConnection, - isAggregate, - }); - } - - const attribute = entity.findAttribute(fieldName); - - if (!attribute) { - throw new Error(`Attribute ${fieldName} not found`); - } - - return this.createPropertyFilter({ - attribute, - relationship, - comparisonValue: value, - isNot, - operator, - }); + return this.parseEntryFilter({ entity, key, value, targetEntity, relationship }); }) ); - return this.wrapMultipleFiltersInLogical(filters); } @@ -482,88 +452,274 @@ export class FilterFactory { if (isUnionEntity(entity)) { return []; } - const filters = filterTruthy( Object.entries(whereFields).flatMap(([key, value]): Filter | Filter[] | undefined => { - const valueAsArray = asArray(value); - if (isLogicalOperator(key)) { - const nestedFilters = valueAsArray.flatMap((nestedWhere) => { - return this.createNodeFilters(entity, nestedWhere); - }); - return new LogicalFilter({ - operation: key, - filters: nestedFilters, - }); - } + return this.parseEntryFilter({ entity, key, value }); + }) + ); + return this.wrapMultipleFiltersInLogical(filters); + } - const { fieldName, operator, isNot, isConnection, isAggregate } = parseWhereField(key); + private parseEntryFilter({ + entity, + key, + value, + targetEntity, + relationship, + }: { + entity: ConcreteEntityAdapter | InterfaceEntityAdapter; + key: string; + value: any; + targetEntity?: ConcreteEntityAdapter; + relationship?: RelationshipAdapter; + }): Filter | Filter[] { + const valueAsArray = asArray(value); + if (isLogicalOperator(key)) { + const nestedFilters = valueAsArray.flatMap((nestedWhere) => { + const nestedOfNestedFilters = Object.entries(nestedWhere).flatMap(([nestedKey, nestedValue]) => { + return asArray( + this.parseEntryFilter({ + entity, + key: nestedKey, + value: nestedValue, + targetEntity, + relationship, + }) + ); + }); - const relationship = entity.findRelationship(fieldName); + return this.wrapMultipleFiltersInLogical(nestedOfNestedFilters); + }); + return new LogicalFilter({ + operation: key, + filters: nestedFilters, + }); + } + const { fieldName, operator, isConnection, isAggregate } = parseWhereField(key); + if (isConcreteEntity(entity)) { + const relationship = entity.findRelationship(fieldName); - if (relationship) { - return this.createRelatedNodeFilters({ - relationship, - value, - operator, - isNot, - isConnection, - isAggregate, - }); + if (relationship) { + return this.createRelatedNodeFilters({ + relationship, + value, + operator, + isConnection, + isAggregate, + }); + } + } else { + const relationshipDeclaration = entity.findRelationshipDeclarations(fieldName); + if (targetEntity && relationshipDeclaration) { + const relationship = relationshipDeclaration.relationshipImplementations.find( + (r) => r.source.name === targetEntity.name + ); + if (!relationship) { + throw new Error(`Relationship ${fieldName} not found`); } + return this.createRelatedNodeFilters({ + relationship, + value, + operator, + isConnection, + isAggregate, + }); + } + if (key === "typename") { + const acceptedEntities = entity.concreteEntities.filter((concreteEntity) => { + return valueAsArray.some((typenameFilterValue) => typenameFilterValue === concreteEntity.name); + }); + return new TypenameFilter(acceptedEntities); + } + } - const attribute = entity.findAttribute(fieldName); + const attribute = entity.findAttribute(fieldName); - if (!attribute) { - if (fieldName === "id" && entity.globalIdField) { - return this.createRelayIdPropertyFilter(entity, isNot, operator, value); - } + if (!isInterfaceEntity(entity) && !attribute) { + if (fieldName === "id" && entity.globalIdField) { + return this.createRelayIdPropertyFilter(entity, operator, value); + } + } + if (!attribute) { + throw new Error(`Attribute ${fieldName} not found`); + } - throw new Error(`Attribute ${fieldName} not found`); - } + // This is a bit hacky, basically skipping cypher fields and federation strings being passed to filterFactory + if (!operator && !attribute.annotations.cypher?.targetEntity && typeof value === "object") { + return this.parseGenericFilters(entity, fieldName, value, relationship); + } - return this.createPropertyFilter({ - attribute, - comparisonValue: value, - isNot, - operator, - }); - }) - ); + return this.createPropertyFilter({ + attribute, + comparisonValue: value, + operator: operator, + relationship, + }); + } - return this.wrapMultipleFiltersInLogical(filters); + private parseGenericFilters( + entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter, + fieldName: string, + value: Record, + relationship?: RelationshipAdapter + ): Filter | Filter[] { + const genericFilters = Object.entries(value).flatMap((filterInput) => { + return this.parseGenericFilter(entity, fieldName, filterInput, relationship); + }); + return this.wrapMultipleFiltersInLogical(genericFilters); + } + + private parseGenericFilter( + entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter, + fieldName: string, + filterInput: [string, any], + relationship?: RelationshipAdapter + ): Filter | Filter[] { + const [rawOperator, value] = filterInput; + if (isLogicalOperator(rawOperator)) { + const nestedFilters = asArray(value).flatMap((nestedWhere) => { + return this.parseGenericFilter(entity, fieldName, nestedWhere, relationship); + }); + return new LogicalFilter({ + operation: rawOperator, + filters: nestedFilters, + }); + } + + if (rawOperator === "distance") { + // Converts new distance filter into the old one to be parsed the same as deprecated syntax + const desugaredInput = this.desugarGenericDistanceOperations(value); + return this.parseGenericFilters(entity, fieldName, desugaredInput, relationship); + } + + const operator = this.parseGenericOperator(rawOperator); + + const attribute = entity.findAttribute(fieldName); + + if (!attribute) { + if (isRelationshipEntity(entity) || isInterfaceEntity(entity)) { + throw new Error("Transpilation error: Expected concrete entity"); + } + if (fieldName === "id" && entity.globalIdField) { + return this.createRelayIdPropertyFilter(entity, operator, value); + } + throw new Error(`Attribute ${fieldName} not found`); + } + const attachedTo = isRelationshipEntity(entity) ? "relationship" : "node"; + const filters = this.createPropertyFilter({ + attribute, + comparisonValue: value, + operator, + attachedTo, + relationship, + }); + return this.wrapMultipleFiltersInLogical(asArray(filters)); + } + + protected parseGenericOperator(key: string): FilterOperator { + // we convert them to the previous format to keep the same translation logic + switch (key) { + case "eq": + return "EQ"; + case "in": + return "IN"; + case "lt": + return "LT"; + case "lte": + return "LTE"; + case "greaterThan": + case "gt": + return "GT"; + case "gte": + return "GTE"; + case "contains": + return "CONTAINS"; + case "startsWith": + return "STARTS_WITH"; + case "endsWith": + return "ENDS_WITH"; + case "matches": + return "MATCHES"; + case "includes": + return "INCLUDES"; + case "distance_eq": // Used for distance -> eq + return "DISTANCE"; + default: + throw new Error(`Invalid operator ${key}`); + } + } + private parseGenericOperatorForAggregation(key: string): AggregationLogicalOperator { + // we convert them to the previous format to keep the same translation logic + switch (key) { + case "eq": + return "EQUAL"; + case "lt": + return "LT"; + case "lte": + return "LTE"; + case "gt": + return "GT"; + case "gte": + return "GTE"; + + default: + throw new Error(`Invalid operator ${key}`); + } + } + + protected convertRelationshipOperatorToLegacyOperator(operator: string): RelationshipWhereOperator { + switch (operator) { + case "some": + return "SOME"; + case "all": + return "ALL"; + case "single": + return "SINGLE"; + case "none": + return "NONE"; + } + throw new Error(`Invalid operator ${operator}`); } private createRelatedNodeFilters({ relationship, value, operator, - isNot, + isConnection, isAggregate, }: { relationship: RelationshipAdapter; - value: any; + value: Record; operator: FilterOperator | undefined; - isNot: boolean; + isConnection: boolean; isAggregate: boolean; }): Filter | Filter[] { if (isAggregate) { return this.createAggregationFilter(relationship, value as AggregateWhereInput); } - if (operator && !isRelationshipOperator(operator)) { + if (!operator) { + const genericFilters = Object.entries(value).flatMap(([quantifier, predicate]) => { + const legacyOperator = this.convertRelationshipOperatorToLegacyOperator(quantifier); + return this.createRelatedNodeFilters({ + relationship, + value: predicate, + operator: legacyOperator, + isConnection, + isAggregate, + }); + }); + return this.wrapMultipleFiltersInLogical(genericFilters); + } + + if (operator && !isLegacyRelationshipOperator(operator)) { throw new Error(`Invalid operator ${operator} for relationship`); } if (isConnection) { - return this.createConnectionFilter(relationship, value as ConnectionWhereArg, { - isNot, - operator, - }); + return this.createConnectionFilter(relationship, value as ConnectionWhereArg, operator); } - return this.createRelationshipFilter(relationship, value as GraphQLWhereArg, { - isNot, - operator, - }); + return this.createRelationshipFilter(relationship, value as GraphQLWhereArg, operator); } private getLogicalOperatorForRelatedNodeFilters( @@ -583,8 +739,8 @@ export class FilterFactory { private createRelayIdPropertyFilter( entity: ConcreteEntityAdapter, - isNot: boolean, - operator: FilterOperator | undefined, + + operator: FilterOperator | undefined = "EQ", value: string ): Filter | Filter[] { const relayIdData = fromGlobalId(value); @@ -609,7 +765,6 @@ export class FilterFactory { return this.createPropertyFilter({ attribute: idAttribute, comparisonValue: id as unknown as GraphQLWhereArg, - isNot, operator, }); } @@ -625,19 +780,23 @@ export class FilterFactory { filters: nestedFilters, }); } - const { fieldName, operator, isNot } = parseWhereField(key); + const { fieldName, operator } = parseWhereField(key); + const attribute = relationship.findAttribute(fieldName); if (!attribute) { + // @declareRelationship path. if (fieldName === relationship.propertiesTypeName) { return this.createEdgeFilters(relationship, value); } return; } + if (!operator) { + return this.parseGenericFilters(relationship, fieldName, value); + } return this.createPropertyFilter({ attribute, comparisonValue: value, - isNot, operator, attachedTo: "relationship", }); @@ -663,13 +822,22 @@ export class FilterFactory { }); return [logicalFilter]; } - const { fieldName, operator, isNot } = parseWhereField(key); + const { fieldName, operator } = parseWhereField(key); - const filterOperator = operator ?? "EQ"; if (fieldName === "count") { + if (!operator) { + return Object.entries(value).map(([key, value]) => { + const operator = this.parseGenericOperator(key); + + return new CountFilter({ + operator: operator, + comparisonValue: value, + }); + }); + } + const countFilter = new CountFilter({ - operator: filterOperator, - isNot, + operator: operator ?? "EQ", comparisonValue: value, }); return [countFilter]; @@ -719,18 +887,75 @@ export class FilterFactory { entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter, relationship?: RelationshipAdapter ): Array { - const filters = Object.entries(where).map(([key, value]) => { + const filters = Object.entries(where).flatMap(([key, value]) => { if (isLogicalOperator(key)) { - return this.createAggregateLogicalFilter(key, value, entity, relationship); + const filters = asArray(value).flatMap((nestedWhere) => { + return this.createAggregationNodeFilters(nestedWhere, entity, relationship); + }); + return new LogicalFilter({ + operation: key, + filters, + }); } // NOTE: if aggregationOperator is undefined, maybe we could return a normal PropertyFilter instead const { fieldName, logicalOperator, aggregationOperator } = parseAggregationWhereFields(key); const attr = entity.findAttribute(fieldName); - if (!attr) throw new Error(`Attribute ${fieldName} not found`); - + if (!attr) { + throw new Error(`Attribute ${fieldName} not found`); + } const attachedTo = entity instanceof RelationshipAdapter ? "relationship" : "node"; + if (!aggregationOperator) { + const filters = Object.entries(value).flatMap(([aggregationOperator, value]) => { + const parsedAggregationOperation = this.parseGenericAggregationOperator(aggregationOperator); + + // NOTE: this part is duplicate of the code used for non-generic operators + return Object.entries(value as Record).map(([operator, value]) => { + const parsedOperator = this.parseGenericOperatorForAggregation(operator); + if (attr.typeHelper.isDuration()) { + return new AggregationDurationFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + } + + if (attr.typeHelper.isDateTime()) { + return new AggregationDateTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + } + + if (attr.typeHelper.isTime()) { + return new AggregationTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + } + + return new AggregationPropertyFilter({ + attribute: attr, + relationship, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + }); + }); + return this.wrapMultipleFiltersInLogical(filters); + } + if (attr.typeHelper.isDuration()) { return new AggregationDurationFilter({ attribute: attr, @@ -741,6 +966,26 @@ export class FilterFactory { }); } + if (attr.typeHelper.isDateTime()) { + return new AggregationDateTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: logicalOperator || "EQUAL", + aggregationOperator: aggregationOperator, + attachedTo, + }); + } + + if (attr.typeHelper.isTime()) { + return new AggregationTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: logicalOperator || "EQUAL", + aggregationOperator: aggregationOperator, + attachedTo, + }); + } + return new AggregationPropertyFilter({ attribute: attr, relationship, @@ -755,7 +1000,7 @@ export class FilterFactory { } /** Returns an array of 0 or 1 elements with the filters wrapped using a logical operator if needed */ - private wrapMultipleFiltersInLogical( + protected wrapMultipleFiltersInLogical( filters: F[], logicalOp: "AND" | "OR" | "XOR" = "AND" ): [F | LogicalFilter] | [] { @@ -775,21 +1020,6 @@ export class FilterFactory { return []; } - private createAggregateLogicalFilter( - operation: "OR" | "AND" | "NOT", - where: GraphQLWhereArg[] | GraphQLWhereArg, - entity: ConcreteEntityAdapter | RelationshipAdapter | InterfaceEntityAdapter, - relationship?: RelationshipAdapter - ): LogicalFilter { - const filters = asArray(where).flatMap((nestedWhere) => { - return this.createAggregationNodeFilters(nestedWhere, entity, relationship); - }); - return new LogicalFilter({ - operation, - filters, - }); - } - // This method identifies if it's possible to achieve MATCH (n)-[r]->(m) WHERE m:Movie Or m:Series rather than MATCH (n)-[r]->(m:Movie) Or MATCH (n)-[r]->(m:Series) // When filters contain a nested relationship filter this is no longer achievable as the relationship definition is not shared between each concrete entity. // For context check TCK test packages/graphql/tests/tck/issues/2709.test.ts --> "should not use a node label so it covers all nodes implementing the interface for connection rel". @@ -798,7 +1028,7 @@ export class FilterFactory { entity: InterfaceEntityAdapter ): boolean { if (where.node) { - const containsUnoptimizableFields = Object.keys(where.node).some((field) => { + const containsUnOptimizableFields = Object.keys(where.node).some((field) => { const { fieldName, isAggregate, isConnection } = parseWhereField(field); if (isAggregate || isConnection) { return true; @@ -809,8 +1039,53 @@ export class FilterFactory { } return false; }); - return !containsUnoptimizableFields; + return !containsUnOptimizableFields; } return true; } + + /** Converts new distance operator into traditional operator **/ + private desugarGenericDistanceOperations(distance: Record & { from: any }): Record { + const point = distance.from; + const targetPoint: Record = {}; + + // eslint-disable-next-line prefer-const + for (let [key, value] of Object.entries(distance)) { + if (key !== "from") { + // We need this fake operator to differentiate distance from point eq in the + // desugaring process. Not needed in other operators because they are always distance based + if (key === "eq") { + key = "distance_eq"; + } + targetPoint[key] = { + distance: value, + point, + }; + } + } + return targetPoint; + } + + private parseGenericAggregationOperator(key: string): AggregationOperator { + // we convert them to the previous format to keep the same translation logic + switch (key) { + case "averageLength": + case "average": + return "AVERAGE"; + case "shortestLength": + case "shortest": + return "SHORTEST"; + case "longestLength": + case "longest": + return "LONGEST"; + case "min": + return "MIN"; + case "max": + return "MAX"; + case "sum": + return "SUM"; + default: + throw new Error(`Invalid aggregation operator ${key}`); + } + } } diff --git a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts index d729628e2b..560f51846a 100644 --- a/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/OperationFactory.ts @@ -26,14 +26,14 @@ import type { ConcreteEntityAdapter } from "../../../schema-model/entity/model-a import type { InterfaceEntityAdapter } from "../../../schema-model/entity/model-adapters/InterfaceEntityAdapter"; import type { UnionEntityAdapter } from "../../../schema-model/entity/model-adapters/UnionEntityAdapter"; import type { RelationshipAdapter } from "../../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import type { GraphQLOptionsArg } from "../../../types"; +import type { GraphQLSortingAndPaginationArgs } from "../../../types"; import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { filterTruthy, isRecord } from "../../../utils/utils"; import type { Filter } from "../ast/filters/Filter"; import type { AggregationOperation } from "../ast/operations/AggregationOperation"; import type { ConnectionReadOperation } from "../ast/operations/ConnectionReadOperation"; -import type { CypherOperation } from "../ast/operations/CypherOperation"; -import type { CypherScalarOperation } from "../ast/operations/CypherScalarOperation"; +import type { CypherAttributeOperation } from "../ast/operations/CypherAttributeOperation"; +import type { CypherEntityOperation } from "../ast/operations/CypherEntityOperation"; import type { ReadOperation } from "../ast/operations/ReadOperation"; import type { CompositeAggregationOperation } from "../ast/operations/composite/CompositeAggregationOperation"; import type { CompositeConnectionReadOperation } from "../ast/operations/composite/CompositeConnectionReadOperation"; @@ -109,30 +109,9 @@ export class OperationsFactory { reference?: any; resolveAsUnwind?: boolean; }): Operation { - if ( - entity && - isConcreteEntity(entity) && - Boolean(entity.annotations.fulltext) && - context.fulltext && - context.resolveTree.args.phrase - ) { - // Handles the new FullText operation as moviesFullText(phrase: "The Matrix") {...} - const indexName = context.fulltext.indexName ?? context.fulltext.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - assertIsConcreteEntity(entity); - return this.fulltextFactory.createFulltextOperation(entity, resolveTree, context); - } - const operationMatch = parseTopLevelOperationField(resolveTree.name, context, entity); switch (operationMatch) { case "READ": { - // handle the deprecated way to do FullText search - if (context.resolveTree.args.fulltext || context.resolveTree.args.phrase) { - assertIsConcreteEntity(entity); - return this.fulltextFactory.createDeprecatedFulltextOperation(entity, resolveTree, context); - } if (!entity) { throw new Error("Entity is required for top level read operations"); } @@ -144,6 +123,16 @@ export class OperationsFactory { reference, }); } + case "FULLTEXT": { + if (!entity) { + throw new Error("Entity is required for top level connection read operations"); + } + if (!isConcreteEntity(entity)) { + throw new Error("Fulltext operations are only supported on concrete entities"); + } + + return this.fulltextFactory.createFulltextOperation(entity, resolveTree, context); + } case "VECTOR": { if (!entity) { throw new Error("Entity is required for top level connection read operations"); @@ -288,7 +277,7 @@ export class OperationsFactory { entity?: EntityAdapter; cypherAttributeField: AttributeAdapter; cypherArguments?: Record; - }): CypherOperation | CompositeCypherOperation | CypherScalarOperation { + }): CypherEntityOperation | CompositeCypherOperation | CypherAttributeOperation { return this.customCypherFactory.createCustomCypherOperation(arg); } /** @@ -378,7 +367,7 @@ export class OperationsFactory { limitArg: number | Integer; offsetArg: number; sortArg: Record[]; - }): GraphQLOptionsArg | undefined { + }): GraphQLSortingAndPaginationArgs | undefined { const limitDirective = isUnionEntity(entity) ? undefined : entity.annotations.limit; let limit: Integer | number | undefined = limitArg ?? limitDirective?.default ?? limitDirective?.max; diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts index ac6148c423..e8fade40ca 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/AggregateFactory.ts @@ -66,7 +66,6 @@ export class AggregateFactory { const selection = new RelationshipSelection({ relationship: entityOrRel, - directed: Boolean(resolveTree.args?.directed ?? true), }); const operation = new AggregationOperation({ diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts index 909933bb81..d2cf75d839 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/ConnectionFactory.ts @@ -68,7 +68,6 @@ export class ConnectionFactory { resolveTree: ResolveTree; context: Neo4jGraphQLTranslationContext; }): CompositeConnectionReadOperation { - const directed = resolveTree.args.directed as boolean | undefined; const resolveTreeWhere: Record = this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree); let nodeWhere: Record; @@ -85,7 +84,6 @@ export class ConnectionFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed, targetOverride: concreteEntity, }); resolveTreeEdgeFields = this.parseConnectionFields({ @@ -165,7 +163,6 @@ export class ConnectionFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: resolveTree.args.directed as boolean | undefined, }); resolveTreeEdgeFields = this.parseConnectionFields({ entityOrRel: relationship, @@ -199,9 +196,8 @@ export class ConnectionFactory { }); } - // eslint-disable-next-line @typescript-eslint/comma-dangle private hydrateConnectionOperationsASTWithSort< - T extends ConnectionReadOperation | CompositeConnectionReadOperation + T extends ConnectionReadOperation | CompositeConnectionReadOperation, >({ entityOrRel, resolveTree, @@ -297,11 +293,11 @@ export class ConnectionFactory { private getConnectionOptions( entity: ConcreteEntityAdapter | InterfaceEntityAdapter, - options: Record + args: Record ): Pick | undefined { const limitDirective = entity.annotations.limit; - let limit: Integer | number | undefined = options?.first ?? limitDirective?.default ?? limitDirective?.max; + let limit: Integer | number | undefined = args?.first ?? limitDirective?.default ?? limitDirective?.max; if (limit instanceof Integer) { limit = limit.toNumber(); } @@ -310,12 +306,12 @@ export class ConnectionFactory { limit = Math.min(limit, maxLimit); } - if (limit === undefined && options.after === undefined && options.sort === undefined) return undefined; + if (limit === undefined && args.after === undefined && args.sort === undefined) return undefined; return { first: limit, - after: options.after, - sort: options.sort, + after: args.after, + sort: args.sort, }; } diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/CustomCypherFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/CustomCypherFactory.ts index 5148664b10..08897ab1b7 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/CustomCypherFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/CustomCypherFactory.ts @@ -22,8 +22,8 @@ import { AttributeAdapter } from "../../../../schema-model/attribute/model-adapt import type { EntityAdapter } from "../../../../schema-model/entity/EntityAdapter"; import type { Neo4jGraphQLTranslationContext } from "../../../../types/neo4j-graphql-translation-context"; import { TypenameFilter } from "../../ast/filters/property-filters/TypenameFilter"; -import { CypherOperation } from "../../ast/operations/CypherOperation"; -import { CypherScalarOperation } from "../../ast/operations/CypherScalarOperation"; +import { CypherAttributeOperation } from "../../ast/operations/CypherAttributeOperation"; +import { CypherEntityOperation } from "../../ast/operations/CypherEntityOperation"; import { CompositeCypherOperation } from "../../ast/operations/composite/CompositeCypherOperation"; import { CompositeReadPartial } from "../../ast/operations/composite/CompositeReadPartial"; import { CustomCypherSelection } from "../../ast/selection/CustomCypherSelection"; @@ -50,17 +50,17 @@ export class CustomCypherFactory { entity?: EntityAdapter; cypherAttributeField: AttributeAdapter; cypherArguments?: Record; - }): CypherOperation | CompositeCypherOperation | CypherScalarOperation { + }): CypherEntityOperation | CompositeCypherOperation | CypherAttributeOperation { const selection = new CustomCypherSelection({ operationField: cypherAttributeField, rawArguments: cypherArguments, isNested: true, }); if (!entity) { - return new CypherScalarOperation(selection, cypherAttributeField, true); + return new CypherAttributeOperation(selection, cypherAttributeField, true); } if (isConcreteEntity(entity)) { - const customCypher = new CypherOperation({ + const customCypher = new CypherEntityOperation({ cypherAttributeField: cypherAttributeField, target: entity, selection, @@ -107,7 +107,7 @@ export class CustomCypherFactory { resolveTree: ResolveTree; context: Neo4jGraphQLTranslationContext; entity?: EntityAdapter; - }): CypherOperation | CompositeCypherOperation | CypherScalarOperation { + }): CypherEntityOperation | CompositeCypherOperation | CypherAttributeOperation { const operationAttribute = context.schemaModel.operations.Query?.findAttribute(resolveTree.name) ?? context.schemaModel.operations.Mutation?.findAttribute(resolveTree.name); @@ -123,10 +123,10 @@ export class CustomCypherFactory { isNested: false, }); if (!entity) { - return new CypherScalarOperation(selection, operationField, false); + return new CypherAttributeOperation(selection, operationField, false); } if (isConcreteEntity(entity)) { - const customCypher = new CypherOperation({ + const customCypher = new CypherEntityOperation({ cypherAttributeField: operationField, target: entity, selection, diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts index 1ebe90cdbc..11b02d2a1b 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/DeleteFactory.ts @@ -27,7 +27,7 @@ import { checkEntityAuthentication } from "../../../authorization/check-authenti import type { Filter } from "../../ast/filters/Filter"; import { DeleteOperation } from "../../ast/operations/DeleteOperation"; import { NodeSelection } from "../../ast/selection/NodeSelection"; -import { DirectedRelationshipSelection } from "../../ast/selection/RelationshipSelection"; +import { RelationshipSelection } from "../../ast/selection/RelationshipSelection"; import { getConcreteEntities } from "../../utils/get-concrete-entities"; import { isInterfaceEntity } from "../../utils/is-interface-entity"; import { isUnionEntity } from "../../utils/is-union-entity"; @@ -204,7 +204,7 @@ export class DeleteFactory { context, }); - const selection = new DirectedRelationshipSelection({ + const selection = new RelationshipSelection({ relationship, optional: true, targetOverride: target, diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/FulltextFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/FulltextFactory.ts index f6c0658f13..d06cc23134 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/FulltextFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/FulltextFactory.ts @@ -17,19 +17,18 @@ * limitations under the License. */ -import Cypher from "@neo4j/cypher-builder"; import type { ResolveTree } from "graphql-parse-resolve-info"; import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter"; import type { Neo4jGraphQLTranslationContext } from "../../../../types/neo4j-graphql-translation-context"; -import { asArray } from "../../../../utils/utils"; import { checkEntityAuthentication } from "../../../authorization/check-authentication"; import { ScoreField } from "../../ast/fields/ScoreField"; import { ScoreFilter } from "../../ast/filters/property-filters/ScoreFilter"; import type { FulltextOptions } from "../../ast/operations/FulltextOperation"; import { FulltextOperation } from "../../ast/operations/FulltextOperation"; import { FulltextSelection } from "../../ast/selection/FulltextSelection"; -import { raiseOnMixedPagination } from "../../utils/raise-on-mixed-pagination"; import type { QueryASTFactory } from "../QueryASTFactory"; +import { findFieldsByNameInFieldsByTypeNameField } from "../parsers/find-fields-by-name-in-fields-by-type-name-field"; +import { getFieldsByTypeName } from "../parsers/get-fields-by-type-name"; export class FulltextFactory { private queryASTFactory: QueryASTFactory; @@ -37,23 +36,14 @@ export class FulltextFactory { constructor(queryASTFactory: QueryASTFactory) { this.queryASTFactory = queryASTFactory; } - /** - * @deprecated This method is deprecated an it will be removed when the deprecate fulltext operation will be removed. - * The is the factory method that parse the deprecated syntax as movies(fulltext: { phrase: "The Matrix" }) {...} - * To parse the new syntax movieFullText(phrase: "The Matrix") {...} use the method createFulltextOperation - * - **/ - public createDeprecatedFulltextOperation( + + public createFulltextOperation( entity: ConcreteEntityAdapter, resolveTree: ResolveTree, context: Neo4jGraphQLTranslationContext ): FulltextOperation { - const resolveTreeWhere: Record = this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree); - - const fieldsByTypeName = resolveTree.fieldsByTypeName; - const fullTextOptions = this.getFulltextOptions(context); - let scoreField: ScoreField | undefined; - let scoreFilter: ScoreFilter | undefined; + const resolveTreeWhere: Record = + this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree) ?? {}; checkEntityAuthentication({ entity: entity.entity, @@ -61,165 +51,71 @@ export class FulltextFactory { context, }); - const selection = new FulltextSelection({ - target: entity, - fulltext: fullTextOptions, - scoreVariable: fullTextOptions.score, - }); - const operation = new FulltextOperation({ - target: entity, - scoreField, - selection, - }); - - if (scoreFilter) { - operation.addFilters(scoreFilter); - } - - this.queryASTFactory.operationsFactory.hydrateOperation({ - operation, - entity, - fieldsByTypeName: fieldsByTypeName, - context, - whereArgs: resolveTreeWhere, - }); - - // Override sort to support score - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - const optionsArg: Record = (resolveTree.args.options ?? {}) as Record; - const sortArg = resolveTree.args.sort ?? optionsArg.sort; - const limitArg = resolveTree.args.limit ?? optionsArg.limit; - const offsetArg = resolveTree.args.offset ?? optionsArg.offset; - raiseOnMixedPagination({ - optionsArg, - sort: resolveTree.args.sort, - limit: resolveTree.args.limit, - offset: resolveTree.args.offset, - }); - const paginationOptions = this.queryASTFactory.operationsFactory.getOptions({ - entity, - limitArg, - offsetArg, - sortArg, - }); - - if (paginationOptions) { - const sort = this.queryASTFactory.sortAndPaginationFactory.createSortFields( - paginationOptions, - entity, - context, - fullTextOptions.score - ); - operation.addSort(...sort); - - const pagination = this.queryASTFactory.sortAndPaginationFactory.createPagination(paginationOptions); - if (pagination) { - operation.addPagination(pagination); - } - } - - return operation; - } - - public createFulltextOperation( - entity: ConcreteEntityAdapter, - resolveTree: ResolveTree, - context: Neo4jGraphQLTranslationContext - ): FulltextOperation { - const fullTextDeprecateOperationFields = - resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.result]; - - if (!fullTextDeprecateOperationFields) { - throw new Error("Transpile error: operation not found"); - } - const resolveTreeWhere: Record = this.queryASTFactory.operationsFactory.getWhereArgs(resolveTree); - - const fullTextOptions = this.getFulltextOptions(context); let scoreField: ScoreField | undefined; - let scoreFilter: ScoreFilter | undefined; - - const scoreWhere = resolveTreeWhere.score; - const targetTypeWhere = resolveTreeWhere[entity.singular] ?? {}; + const fulltextConnectionFields = resolveTree.fieldsByTypeName[entity.operations.fulltextTypeNames.connection]; - const scoreRawField = fullTextDeprecateOperationFields.score; + if (!fulltextConnectionFields) { + throw new Error("Fulltext result field not found"); + } - const nestedResolveTree: Record = fullTextDeprecateOperationFields[entity.singular] ?? {}; + const filteredResolveTreeEdges = findFieldsByNameInFieldsByTypeNameField(fulltextConnectionFields, "edges"); + const edgeFields = getFieldsByTypeName(filteredResolveTreeEdges, entity.operations.fulltextTypeNames.edge); + const scoreFields = findFieldsByNameInFieldsByTypeNameField(edgeFields, "score"); - if (scoreRawField) { + // We only care about the first score field + if (scoreFields.length > 0 && scoreFields[0] && context.fulltext) { scoreField = new ScoreField({ - alias: scoreRawField.alias, - score: fullTextOptions.score, - }); - } - if (scoreWhere) { - scoreFilter = new ScoreFilter({ - scoreVariable: fullTextOptions.score, - min: scoreWhere.min, - max: scoreWhere.max, + alias: scoreFields[0].alias, + score: context.fulltext.scoreVariable, }); } - checkEntityAuthentication({ - entity: entity.entity, - targetOperations: ["READ"], - context, - }); - - const selection = new FulltextSelection({ - target: entity, - fulltext: fullTextOptions, - scoreVariable: fullTextOptions.score, - }); const operation = new FulltextOperation({ target: entity, scoreField, - selection, + selection: this.getFulltextSelection(entity, context), }); - if (scoreFilter) { - operation.addFilters(scoreFilter); - } - const fieldsByTypeName = nestedResolveTree.fieldsByTypeName ?? {}; - this.queryASTFactory.operationsFactory.hydrateOperation({ + const concreteEdgeFields = getFieldsByTypeName( + filteredResolveTreeEdges, + entity.operations.fulltextTypeNames.edge + ); + + this.addFulltextScoreFilter({ operation, - entity, - fieldsByTypeName, context, - whereArgs: targetTypeWhere, + whereArgs: resolveTreeWhere, }); - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - const optionsArg: Record = (resolveTree.args.options ?? {}) as Record; - // Override sort to support score and other fields as: { score: "DESC", movie: { title: DESC }} - const sortArg = asArray(resolveTree.args.sort ?? optionsArg.sort).map( - (field) => field[entity.singular] ?? field - ); - const limitArg = resolveTree.args.limit ?? optionsArg.limit; - const offsetArg = resolveTree.args.offset ?? optionsArg.offset; - - const paginationOptions = this.queryASTFactory.operationsFactory.getOptions({ - entity, - limitArg, - offsetArg, - sortArg, + this.queryASTFactory.operationsFactory.hydrateConnectionOperation({ + target: entity, + resolveTree: resolveTree, + context, + operation, + whereArgs: resolveTreeWhere, + resolveTreeEdgeFields: concreteEdgeFields, }); - if (paginationOptions) { - const sort = this.queryASTFactory.sortAndPaginationFactory.createSortFields( - paginationOptions, - entity, - context, - fullTextOptions.score - ); - operation.addSort(...sort); + return operation; + } - const pagination = this.queryASTFactory.sortAndPaginationFactory.createPagination(paginationOptions); - if (pagination) { - operation.addPagination(pagination); - } + private addFulltextScoreFilter({ + operation, + whereArgs, + context, + }: { + operation: FulltextOperation; + whereArgs: Record; + context: Neo4jGraphQLTranslationContext; + }): void { + if (whereArgs.score && context?.fulltext) { + const scoreFilter = new ScoreFilter({ + scoreVariable: context.fulltext.scoreVariable, + min: whereArgs.score.min, + max: whereArgs.score.max, + }); + operation.addFilters(scoreFilter); } - - return operation; } public getFulltextSelection( @@ -229,38 +125,25 @@ export class FulltextFactory { const fulltextOptions = this.getFulltextOptions(context); return new FulltextSelection({ target: entity, - fulltext: fulltextOptions, + fulltextOptions, scoreVariable: fulltextOptions.score, }); } private getFulltextOptions(context: Neo4jGraphQLTranslationContext): FulltextOptions { - if (context.fulltext) { - const indexName = context.fulltext.indexName ?? context.fulltext.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - const phrase = context.resolveTree.args.phrase; - if (!phrase || typeof phrase !== "string") { - throw new Error("Invalid phrase"); - } - - return { - index: indexName, - phrase, - score: context.fulltext.scoreVariable, - }; + if (!context.fulltext) { + throw new Error("Fulltext context is missing"); } - const entries = Object.entries(context.resolveTree.args.fulltext || {}); - if (entries.length > 1) { - throw new Error("Can only call one search at any given time"); + const phrase = context.resolveTree.args.phrase; + if (!phrase || typeof phrase !== "string") { + throw new Error("Invalid phrase"); } - const [indexName, indexInput] = entries[0] as [string, { phrase: string }]; + return { - index: indexName, - phrase: indexInput.phrase, - score: new Cypher.Variable(), + index: context.fulltext.index, + phrase, + score: context.fulltext.scoreVariable, }; } } diff --git a/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts b/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts index 88c5a5c1be..f7f298c955 100644 --- a/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/Operations/ReadFactory.ts @@ -35,7 +35,6 @@ import { RelationshipSelection } from "../../ast/selection/RelationshipSelection import { getConcreteEntities } from "../../utils/get-concrete-entities"; import { getConcreteWhere } from "../../utils/get-concrete-where"; import { isConcreteEntity } from "../../utils/is-concrete-entity"; -import { raiseOnMixedPagination } from "../../utils/raise-on-mixed-pagination"; import type { QueryASTFactory } from "../QueryASTFactory"; export class ReadFactory { @@ -76,7 +75,6 @@ export class ReadFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: Boolean(resolveTree.args?.directed ?? true), }); } else { selection = new NodeSelection({ @@ -106,7 +104,6 @@ export class ReadFactory { if (relationship) { selection = new RelationshipSelection({ relationship, - directed: Boolean(resolveTree.args?.directed ?? true), targetOverride: concreteEntity, }); } else { @@ -158,17 +155,9 @@ export class ReadFactory { whereArgs: Record | Filter[]; partialOf?: InterfaceEntityAdapter | UnionEntityAdapter; }): T { - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - const optionsArg: Record = (resolveTree.args.options ?? {}) as Record; - const sortArg = resolveTree.args.sort ?? optionsArg.sort; - const limitArg = resolveTree.args.limit ?? optionsArg.limit; - const offsetArg = resolveTree.args.offset ?? optionsArg.offset; - raiseOnMixedPagination({ - optionsArg, - sort: resolveTree.args.sort, - limit: resolveTree.args.limit, - offset: resolveTree.args.offset, - }); + const sortArg = resolveTree.args.sort; + const limitArg = resolveTree.args.limit; + const offsetArg = resolveTree.args.offset; const paginationArgs: Record = { limit: limitArg, offset: offsetArg, sort: sortArg }; @@ -189,17 +178,10 @@ export class ReadFactory { resolveTree: ResolveTree, context: Neo4jGraphQLTranslationContext ) { - // SOFT_DEPRECATION: OPTIONS-ARGUMENT - const optionsArg: Record = (resolveTree.args.options ?? {}) as Record; - const sortArg = resolveTree.args.sort ?? optionsArg.sort; - const limitArg = resolveTree.args.limit ?? optionsArg.limit; - const offsetArg = resolveTree.args.offset ?? optionsArg.offset; - raiseOnMixedPagination({ - optionsArg, - sort: resolveTree.args.sort, - limit: resolveTree.args.limit, - offset: resolveTree.args.offset, - }); + const sortArg = resolveTree.args.sort as Record[]; + const limitArg = resolveTree.args.limit as number; + const offsetArg = resolveTree.args.offset as number; + const options = this.queryASTFactory.operationsFactory.getOptions({ entity, sortArg, limitArg, offsetArg }); if (options) { const sort = this.queryASTFactory.sortAndPaginationFactory.createSortFields(options, entity, context); diff --git a/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts b/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts index 490659e159..186687c724 100644 --- a/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/SortAndPaginationFactory.ts @@ -17,14 +17,18 @@ * limitations under the License. */ -import type Cypher from "@neo4j/cypher-builder"; import { SCORE_FIELD } from "../../../constants"; import type { EntityAdapter } from "../../../schema-model/entity/EntityAdapter"; import { RelationshipAdapter } from "../../../schema-model/relationship/model-adapters/RelationshipAdapter"; -import type { ConnectionSortArg, GraphQLOptionsArg, GraphQLSortArg, NestedGraphQLSortArg } from "../../../types"; +import type { + ConnectionSortArg, + GraphQLSortArg, + GraphQLSortingAndPaginationArgs, + NestedGraphQLSortArg, +} from "../../../types"; import type { Neo4jGraphQLTranslationContext } from "../../../types/neo4j-graphql-translation-context"; import { asArray } from "../../../utils/utils"; -import { CypherScalarOperation } from "../ast/operations/CypherScalarOperation"; +import { CypherAttributeOperation } from "../ast/operations/CypherAttributeOperation"; import { Pagination } from "../ast/pagination/Pagination"; import { CypherPropertySort } from "../ast/sort/CypherPropertySort"; import { PropertySort } from "../ast/sort/PropertySort"; @@ -41,14 +45,13 @@ export class SortAndPaginationFactory { this.queryASTFactory = queryASTFactory; } public createSortFields( - options: GraphQLOptionsArg, + options: GraphQLSortingAndPaginationArgs, entity: EntityAdapter | RelationshipAdapter, - context: Neo4jGraphQLTranslationContext, - scoreVariable?: Cypher.Variable + context: Neo4jGraphQLTranslationContext ): Sort[] { // SOFT_DEPRECATION: OPTIONS-ARGUMENT return asArray(options.sort).flatMap((s) => { - return this.createPropertySort({ optionArg: s, entity, context, scoreVariable }); + return this.createPropertySort({ optionArg: s, entity, context }); }); } @@ -80,12 +83,22 @@ export class SortAndPaginationFactory { context, }); - if (options[SCORE_FIELD] && context?.vector) { - const scoreSort = new ScoreSort({ - scoreVariable: context.vector.scoreVariable, - direction: options[SCORE_FIELD], - }); - nodeSortFields.push(scoreSort); + if (options[SCORE_FIELD]) { + if (context.vector) { + nodeSortFields.push( + new ScoreSort({ + scoreVariable: context.vector.scoreVariable, + direction: options[SCORE_FIELD], + }) + ); + } else if (context.fulltext) { + nodeSortFields.push( + new ScoreSort({ + scoreVariable: context.fulltext.scoreVariable, + direction: options[SCORE_FIELD], + }) + ); + } } return { @@ -94,11 +107,11 @@ export class SortAndPaginationFactory { }; } - public createPagination(options: GraphQLOptionsArg): Pagination | undefined { - if (options.limit || options.offset) { + public createPagination(args: GraphQLSortingAndPaginationArgs): Pagination | undefined { + if (args.limit || args.offset) { return new Pagination({ - skip: options.offset, - limit: options.limit, + skip: args.offset, + limit: args.limit, }); } } @@ -107,12 +120,10 @@ export class SortAndPaginationFactory { optionArg, entity, context, - scoreVariable, }: { optionArg: GraphQLSortArg | NestedGraphQLSortArg; entity: EntityAdapter | RelationshipAdapter; context: Neo4jGraphQLTranslationContext; - scoreVariable?: Cypher.Variable; }): Sort[] { if (isUnionEntity(entity)) { return []; @@ -130,19 +141,10 @@ export class SortAndPaginationFactory { optionArg: optionArg[entity.propertiesTypeName] as GraphQLSortArg, entity, context, - scoreVariable, }); } return Object.entries(optionArg).map(([fieldName, sortDir]) => { - // TODO: fix conflict with a "score" fieldname - if (fieldName === SCORE_FIELD && scoreVariable) { - return new ScoreSort({ - scoreVariable, - direction: sortDir, - }); - } - const attribute = entity.findAttribute(fieldName); if (!attribute) { throw new Error(`no filter attribute ${fieldName}`); @@ -152,7 +154,7 @@ export class SortAndPaginationFactory { context, cypherAttributeField: attribute, }); - if (!(cypherOperation instanceof CypherScalarOperation)) { + if (!(cypherOperation instanceof CypherAttributeOperation)) { throw new Error("Transpile error: sorting is supported only for @cypher scalar properties"); } return new CypherPropertySort({ diff --git a/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.test.ts b/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.test.ts index eb39944f24..d0a4ebe1d4 100644 --- a/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.test.ts +++ b/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.test.ts @@ -23,7 +23,6 @@ import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/mode import { UNSUPPORTED_REASON_ABSTRACT_TYPES, UNSUPPORTED_REASON_CONNECT, - UNSUPPORTED_REASON_CONNECT_OR_CREATE, UNSUPPORTED_REASON_POPULATED_BY, isUnwindCreateSupported, } from "./is-unwind-create-supported"; @@ -89,26 +88,6 @@ describe("isUnwindCreateSupported", () => { expect(reason).toBe(UNSUPPORTED_REASON_CONNECT); }); - test("valid typeDefs, not supported input (connectOrCreate)", () => { - const schemaModel = new SchemaModelBuilder(simpleTypeDefs).instance(); - const context = new ContextBuilder().instance(); - const MovieAdapter = schemaModel.getConcreteEntityAdapter("Movie"); - const input = [ - { title: "The Matrix", actors: { create: [{ node: { name: "actor 1" } }] } }, - { - title: "The Matrix 2", - actors: { - connectOrCreate: [ - { where: { node: { id: "actor-2-id" } }, onCreate: { node: { name: "actor 2" } } }, - ], - }, - }, - ]; - const { isSupported, reason } = isUnwindCreateSupported(MovieAdapter as ConcreteEntityAdapter, input, context); - expect(isSupported).toBeFalsy(); - expect(reason).toBe(UNSUPPORTED_REASON_CONNECT_OR_CREATE); - }); - test("valid typeDefs, not supported nested input (connect)", () => { const schemaModel = new SchemaModelBuilder(simpleTypeDefs).instance(); const context = new ContextBuilder().instance(); diff --git a/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.ts b/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.ts index 5bdfd684ac..9ed58eaee3 100644 --- a/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.ts +++ b/packages/graphql/src/translate/queryAST/factory/parsers/is-unwind-create-supported.ts @@ -28,7 +28,6 @@ export const UNSUPPORTED_REASON_SUBSCRIPTION = "Unwind create optimization does export const UNSUPPORTED_REASON_ABSTRACT_TYPES = "Abstract types are not yet supported"; export const UNSUPPORTED_REASON_POPULATED_BY = "Annotation: populatedBy is not yet supported"; export const UNSUPPORTED_REASON_CONNECT = "Operation: connect is not yet supported"; -export const UNSUPPORTED_REASON_CONNECT_OR_CREATE = "Operation: connectOrCreate is not yet supported"; type UnwindCreateSupported = { isSupported: boolean; @@ -118,11 +117,5 @@ function checkOperation(args: Record): UnwindCreateSupported { }; } - if (args.connectOrCreate) { - return { - isSupported: false, - reason: UNSUPPORTED_REASON_CONNECT_OR_CREATE, - }; - } return SUPPORTED; } diff --git a/packages/graphql/src/translate/queryAST/factory/parsers/parse-operation-fields.ts b/packages/graphql/src/translate/queryAST/factory/parsers/parse-operation-fields.ts index 05d12701bb..c6a303617a 100644 --- a/packages/graphql/src/translate/queryAST/factory/parsers/parse-operation-fields.ts +++ b/packages/graphql/src/translate/queryAST/factory/parsers/parse-operation-fields.ts @@ -34,6 +34,7 @@ export type TopLevelOperationFieldMatch = | "UPDATE" | "DELETE" | "CUSTOM_CYPHER" + | "FULLTEXT" | "VECTOR"; export function parseTopLevelOperationField( @@ -44,6 +45,9 @@ export function parseTopLevelOperationField( if (!entityAdapter) { return "CUSTOM_CYPHER"; } + if (context.fulltext) { + return "FULLTEXT"; + } if (context.vector) { return "VECTOR"; } diff --git a/packages/graphql/src/translate/queryAST/factory/parsers/parse-where-field.ts b/packages/graphql/src/translate/queryAST/factory/parsers/parse-where-field.ts index dd68c2dab7..7893b0b115 100644 --- a/packages/graphql/src/translate/queryAST/factory/parsers/parse-where-field.ts +++ b/packages/graphql/src/translate/queryAST/factory/parsers/parse-where-field.ts @@ -17,20 +17,19 @@ * limitations under the License. */ -import type { FilterOperator, LogicalOperators } from "../../ast/filters/Filter"; +import type { FilterOperator } from "../../ast/filters/Filter"; export type WhereRegexGroups = { fieldName: string; isAggregate: boolean; operator: FilterOperator | undefined; prefix?: string; - isNot: boolean; isConnection: boolean; }; - +// This regex is only valid for the non generic operators const whereRegEx = - /(?\w*\.)?(?[_A-Za-z]\w*?)(?Connection)?(?Aggregate)?(?:_(?NOT|NOT_IN|IN|NOT_INCLUDES|INCLUDES|MATCHES|NOT_CONTAINS|CONTAINS|NOT_STARTS_WITH|STARTS_WITH|NOT_ENDS_WITH|ENDS_WITH|EQ|LT|LTE|GT|GTE|DISTANCE|ALL|NONE|SINGLE|SOME))?$/; + /(?\w*\.)?(?[_A-Za-z]\w*?)(?Connection)?(?Aggregate)?(?:_(?IN|INCLUDES|MATCHES|CONTAINS|STARTS_WITH|ENDS_WITH|EQ|LT|LTE|GT|GTE|DISTANCE|ALL|NONE|SINGLE|SOME))?$/; export function parseWhereField(field: string): WhereRegexGroups { const match = whereRegEx.exec(field); @@ -43,48 +42,17 @@ export function parseWhereField(field: string): WhereRegexGroups { isConnection?: string; }; - let isNot = false; - let operator = undefined as FilterOperator | undefined; - - if (matchGroups.operator) { - const notSplit = matchGroups.operator.split("NOT_"); - if (notSplit.length === 2) { - isNot = true; - operator = notSplit[1] as FilterOperator; - } else if (matchGroups.operator === "NOT" || matchGroups.operator === "NONE") { - isNot = true; - if (matchGroups.operator === "NONE") { - operator = notSplit[0] as FilterOperator; - } - } else { - operator = notSplit[0] as FilterOperator; - } - } + const operator = match?.groups?.operator as FilterOperator | undefined; return { fieldName: matchGroups.fieldName, isAggregate: Boolean(matchGroups.isAggregate), operator, - isNot, prefix: matchGroups.prefix, isConnection: Boolean(matchGroups.isConnection), }; } -type ConnectionWhereArgField = { - isNot: boolean; - fieldName: "node" | "edge" | LogicalOperators; -}; - -export function parseConnectionWhereFields(key: string): ConnectionWhereArgField { - const splitKey = key.split("_NOT"); - const isNot = splitKey.length > 1; - return { - fieldName: splitKey[0] as "node" | "edge" | LogicalOperators, - isNot, - }; -} - export const aggregationFieldRegEx = /(?[_A-Za-z]\w*?)(?:_(?AVERAGE|MAX|MIN|SUM|SHORTEST|LONGEST))?(?:_LENGTH)?(?:_(?EQUAL|GT|GTE|LT|LTE))?$/; diff --git a/packages/graphql/src/translate/translate-create.ts b/packages/graphql/src/translate/translate-create.ts index 062e12e408..9c5b852971 100644 --- a/packages/graphql/src/translate/translate-create.ts +++ b/packages/graphql/src/translate/translate-create.ts @@ -64,7 +64,11 @@ export default async function translateCreate({ const { createStrs, params } = mutationInputs.reduce( (res, input, index) => { - const varName = varNameStrs[index] as string; + const varName = varNameStrs[index]; + if (!varName) { + throw new Error("Expected varName to be defined"); + } + const create = [`CALL {`]; const withVars = [varName]; projectionWith.push(varName); @@ -80,7 +84,6 @@ export default async function translateCreate({ context, varName, withVars, - includeRelationshipValidation: true, topLevelNodeVariable: varName, callbackBucket, }); diff --git a/packages/graphql/src/translate/translate-top-level-match.ts b/packages/graphql/src/translate/translate-top-level-match.ts index 17839ad50f..2a70b8bb3e 100644 --- a/packages/graphql/src/translate/translate-top-level-match.ts +++ b/packages/graphql/src/translate/translate-top-level-match.ts @@ -19,7 +19,6 @@ import Cypher from "@neo4j/cypher-builder"; import type { Node } from "../classes"; -import { SCORE_FIELD } from "../constants"; import type { AuthorizationOperation } from "../schema-model/annotation/AuthorizationAnnotation"; import type { GraphQLWhereArg } from "../types"; import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; @@ -75,29 +74,8 @@ function createMatchClause({ operation: AuthorizationOperation; where: GraphQLWhereArg | undefined; }): CreateMatchClauseReturn { - const { resolveTree } = context; - const fulltextInput = (resolveTree.args.fulltext || {}) as Record; - let matchClause: Cypher.Match | Cypher.Yield = new Cypher.Match(matchPattern); - let whereOperators: Cypher.Predicate[] = []; - - // TODO: removed deprecated fulltext translation - const entries = Object.entries(fulltextInput); - if (entries.length) { - if (entries.length > 1) { - throw new Error("Can only call one search at any given time"); - } - const [indexName, indexInput] = entries[0] as [string, { phrase: string }]; - const phraseParam = new Cypher.Param(indexInput.phrase); - - matchClause = Cypher.db.index.fulltext.queryNodes(indexName, phraseParam).yield(["node", matchNode]); - - whereOperators = node.getLabels(context).map((label) => { - return Cypher.in(new Cypher.Param(label), Cypher.labels(matchNode)); - }); - } else if (context.fulltext) { - ({ matchClause, whereOperators } = createFulltextMatchClause(matchNode, where, node, context)); - where = where?.[node.singular]; - } + const matchClause: Cypher.Match | Cypher.Yield = new Cypher.Match(matchPattern); + const whereOperators: Cypher.Predicate[] = []; let whereClause: Cypher.Match | Cypher.Yield | Cypher.With | undefined; @@ -166,51 +144,3 @@ function createMatchClause({ whereClause, }; } - -function createFulltextMatchClause( - matchNode: Cypher.Node, - whereInput: GraphQLWhereArg | undefined, - node: Node, - context: Neo4jGraphQLTranslationContext -): { - matchClause: Cypher.Yield; - whereOperators: Cypher.Predicate[]; -} { - if (!context.fulltext) { - throw new Error("Full-text context not defined"); - } - - // TODO: remove indexName assignment and undefined check once the name argument has been removed. - const indexName = context.fulltext.indexName || context.fulltext.name; - if (indexName === undefined) { - throw new Error("The name of the fulltext index should be defined using the indexName argument."); - } - const phraseParam = new Cypher.Param(context.resolveTree.args.phrase); - const scoreVar = context.fulltext.scoreVariable; - - const matchClause = Cypher.db.index.fulltext - .queryNodes(indexName, phraseParam) - .yield(["node", matchNode], ["score", scoreVar]); - - const expectedLabels = node.getLabels(context); - const labelsChecks = matchNode.hasLabels(...expectedLabels); - const whereOperators: Cypher.Predicate[] = []; - - if (whereInput?.[SCORE_FIELD]) { - if (whereInput[SCORE_FIELD].min || whereInput[SCORE_FIELD].min === 0) { - const scoreMinOp = Cypher.gte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].min)); - if (scoreMinOp) whereOperators.push(scoreMinOp); - } - if (whereInput[SCORE_FIELD].max || whereInput[SCORE_FIELD].max === 0) { - const scoreMaxOp = Cypher.lte(scoreVar, new Cypher.Param(whereInput[SCORE_FIELD].max)); - if (scoreMaxOp) whereOperators.push(scoreMaxOp); - } - } - - if (labelsChecks) whereOperators.push(labelsChecks); - - return { - matchClause, - whereOperators, - }; -} diff --git a/packages/graphql/src/translate/translate-update.ts b/packages/graphql/src/translate/translate-update.ts index 546e6ef9ae..33e86d05f3 100644 --- a/packages/graphql/src/translate/translate-update.ts +++ b/packages/graphql/src/translate/translate-update.ts @@ -26,11 +26,9 @@ import type { GraphQLWhereArg, RelationField } from "../types"; import type { Neo4jGraphQLTranslationContext } from "../types/neo4j-graphql-translation-context"; import { compileCypher } from "../utils/compile-cypher"; import createConnectAndParams from "./create-connect-and-params"; -import { createConnectOrCreateAndParams } from "./create-connect-or-create-and-params"; import createCreateAndParams from "./create-create-and-params"; import createDeleteAndParams from "./create-delete-and-params"; import createDisconnectAndParams from "./create-disconnect-and-params"; -import { createRelationshipValidationString } from "./create-relationship-validation-string"; import { createSetRelationshipProperties } from "./create-set-relationship-properties"; import createUpdateAndParams from "./create-update-and-params"; import { QueryASTContext, QueryASTEnv } from "./queryAST/ast/QueryASTContext"; @@ -53,7 +51,6 @@ export default async function translateUpdate({ const disconnectInput = resolveTree.args.disconnect; const createInput = resolveTree.args.create; const deleteInput = resolveTree.args.delete; - const connectOrCreateInput = resolveTree.args.connectOrCreate; const varName = "this"; const callbackBucket: CallbackBucket = new CallbackBucket(context); const withVars = [varName]; @@ -64,7 +61,6 @@ export default async function translateUpdate({ const disconnectStrs: string[] = []; const createStrs: string[] = []; let deleteStr = ""; - const assumeReconnecting = Boolean(connectInput) && Boolean(disconnectInput); const matchNode = new Cypher.NamedNode(varName); const where = resolveTree.args.where as GraphQLWhereArg | undefined; const matchPattern = new Cypher.Pattern(matchNode, { labels: node.getLabels(context) }); @@ -174,7 +170,6 @@ export default async function translateUpdate({ parentVar: varName, withVars, parameterPrefix: `${resolveTree.name}.args.update`, - includeRelationshipValidation: false, }); [updateStr] = updateAndParams; cypherParams = { @@ -239,7 +234,6 @@ export default async function translateUpdate({ withVars, parentNode: node, labelOverride: "", - includeRelationshipValidation: !!assumeReconnecting, source: "UPDATE", }); connectStrs.push(connectAndParams[0]); @@ -266,42 +260,6 @@ export default async function translateUpdate({ }); } - if (connectOrCreateInput) { - Object.entries(connectOrCreateInput).forEach(([key, input]) => { - const relationField = node.relationFields.find((x) => key === x.fieldName) as RelationField; - - const refNodes: Node[] = []; - - if (relationField.union) { - Object.keys(input).forEach((unionTypeName) => { - refNodes.push(context.nodes.find((x) => x.name === unionTypeName) as Node); - }); - } else if (relationField.interface) { - relationField.interface?.implementations?.forEach((implementationName) => { - refNodes.push(context.nodes.find((x) => x.name === implementationName) as Node); - }); - } else { - refNodes.push(context.nodes.find((x) => x.name === relationField.typeMeta.name) as Node); - } - - refNodes.forEach((refNode) => { - const { cypher, params } = createConnectOrCreateAndParams({ - input: input[refNode.name] || input, // Deals with different input from update -> connectOrCreate - varName: `${varName}_connectOrCreate_${key}${relationField.union ? `_${refNode.name}` : ""}`, - parentVar: varName, - relationField, - refNode, - node, - context, - withVars, - callbackBucket, - }); - connectStrs.push(cypher); - cypherParams = { ...cypherParams, ...params }; - }); - }); - } - if (createInput) { Object.entries(createInput).forEach((entry) => { const relationField = node.relationFields.find((x) => entry[0] === x.fieldName) as RelationField; @@ -390,7 +348,6 @@ export default async function translateUpdate({ input: create.node, varName: nodeName, withVars: [...withVars, nodeName], - includeRelationshipValidation: false, }); createStrs.push(nestedCreate); cypherParams = { ...cypherParams, ...params }; @@ -449,8 +406,6 @@ export default async function translateUpdate({ ? Cypher.utils.concat(...queryASTResult.clauses) : new Cypher.Return(new Cypher.Literal("Query cannot conclude with CALL")); - const relationshipValidationStr = createRelationshipValidationString({ node, context, varName }); - const updateQuery = new Cypher.Raw((env) => { const cypher = [ matchAndWhereStr, @@ -468,7 +423,6 @@ export default async function translateUpdate({ ? [`WITH *`] : []), // When FOREACH is the last line of update 'Neo4jError: WITH is required between FOREACH and CALL' - ...(relationshipValidationStr ? [`WITH *`, relationshipValidationStr] : []), ...connectionStrs, ...interfaceStrs, compileCypher(projectionStatements, env), diff --git a/packages/graphql/src/translate/utils/get-mutation-field-statements.ts b/packages/graphql/src/translate/utils/get-mutation-field-statements.ts index e2f2355a76..12814ae6e7 100644 --- a/packages/graphql/src/translate/utils/get-mutation-field-statements.ts +++ b/packages/graphql/src/translate/utils/get-mutation-field-statements.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import type { Node, Relationship } from "../../classes"; +import { Neo4jGraphQLError, type Node, type Relationship } from "../../classes"; import { SPATIAL_TYPES } from "../../constants"; import mapToDbProperty from "../../utils/map-to-db-property"; import { buildMathStatements, matchMathField, mathDescriptorBuilder } from "./math"; @@ -36,6 +36,7 @@ export function getMutationFieldStatements({ varName, value, withVars, + isUpdateOperation = false, }: { nodeOrRel: Node | Relationship; param: string; @@ -43,24 +44,48 @@ export function getMutationFieldStatements({ varName: string; value: any; withVars: string[]; + isUpdateOperation?: boolean; }): string { const strs: string[] = []; const { settableField, operator } = parseMutableField(nodeOrRel, key); + if (!operator && isUpdateOperation) { + const result = getMutationFieldStatementsForGenericOperator({ + nodeOrRel, + param, + key, + varName, + operations: value, + withVars, + }); + return result; + } + if (settableField) { const dbFieldName = mapToDbProperty(nodeOrRel, settableField.fieldName); if (settableField.typeMeta.required && value === null) { throw new Error(`Cannot set non-nullable field ${nodeOrRel.name}.${settableField.fieldName} to null`); } - switch (operator) { + switch (operator ?? "SET") { case "SET": { const isSpatial = SPATIAL_TYPES.includes(settableField.typeMeta.name); + const isZonedTemporal = ["DateTime", "Time"].includes(settableField.typeMeta.name); if (isSpatial) { if (settableField.typeMeta.array) { strs.push(`SET ${varName}.${dbFieldName} = [p in $${param} | point(p)]`); } else { strs.push(`SET ${varName}.${dbFieldName} = point($${param})`); } + } else if (isZonedTemporal) { + if (settableField.typeMeta.array) { + strs.push( + `SET ${varName}.${dbFieldName} = [t in $${param} | ${settableField.typeMeta.name.toLowerCase()}(t)]` + ); + } else { + strs.push( + `SET ${varName}.${dbFieldName} = ${settableField.typeMeta.name.toLowerCase()}($${param})` + ); + } } else { strs.push(`SET ${varName}.${dbFieldName} = $${param}`); } @@ -81,11 +106,20 @@ export function getMutationFieldStatements({ } case "PUSH": { const pointArrayField = nodeOrRel.pointFields.find((x) => x.fieldName === settableField.fieldName); + const zonedTemporalArrayField = nodeOrRel.temporalFields.find( + (x) => + x.fieldName === settableField.fieldName && + ["DateTime", "Time"].includes(settableField.typeMeta.name) + ); if (pointArrayField) { strs.push( `SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + [p in $${param} | point(p)]` ); + } else if (zonedTemporalArrayField) { + strs.push( + `SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + [t in $${param} | ${settableField.typeMeta.name.toLowerCase()}(t)]` + ); } else { strs.push(`SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + $${param}`); } @@ -100,3 +134,66 @@ export function getMutationFieldStatements({ } return strs.join("\n"); } + +// Converts generic operator into cypher statements using the deprecated syntax as intermediate step +function getMutationFieldStatementsForGenericOperator({ + nodeOrRel, + param, + key, + varName, + operations, + withVars, +}: { + nodeOrRel: Node | Relationship; + param: string; + key: string; + varName: string; + operations: any; + withVars: string[]; +}): string { + if (Object.entries(operations).length > 1) { + throw new Neo4jGraphQLError( + `Conflicting modification of field ${key}: ${Object.keys(operations) + .map((n) => `[[${n}]]`) + .join(", ")} on type ${nodeOrRel.name}` + ); + } + + return Object.entries(operations) + .map(([operator, value]) => { + return getMutationFieldStatements({ + nodeOrRel, + param: `${param}.${operator}`, + key: `${key}_${newOperatorToDeprecated(operator)}`, + varName, + withVars, + value, + }); + }) + .join("\n"); +} + +function newOperatorToDeprecated(op: string): string { + switch (op) { + case "set": + return "SET"; + case "increment": + return "INCREMENT"; + case "decrement": + return "DECREMENT"; + case "add": + return "ADD"; + case "subtract": + return "SUBTRACT"; + case "divide": + return "DIVIDE"; + case "multiply": + return "MULTIPLY"; + case "push": + return "PUSH"; + case "pop": + return "POP"; + default: + throw new Error(`Unknown generic mutation operator ${op}`); + } +} diff --git a/packages/graphql/src/translate/utils/get-relationship-direction.ts b/packages/graphql/src/translate/utils/get-relationship-direction.ts new file mode 100644 index 0000000000..d926d62ee1 --- /dev/null +++ b/packages/graphql/src/translate/utils/get-relationship-direction.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RelationshipQueryDirectionOption } from "../../constants"; +import type { RelationField } from "../../types"; + +export function getRelationshipDirection(relationshipField: RelationField): { inStr: string; outStr: string } { + const inStr = + relationshipField.direction === "IN" && + relationshipField.queryDirection !== RelationshipQueryDirectionOption.UNDIRECTED + ? "<-" + : "-"; + const outStr = + relationshipField.direction === "OUT" && + relationshipField.queryDirection !== RelationshipQueryDirectionOption.UNDIRECTED + ? "->" + : "-"; + + return { inStr, outStr }; +} diff --git a/packages/graphql/src/translate/utils/parse-mutable-field.ts b/packages/graphql/src/translate/utils/parse-mutable-field.ts index 6242633866..0501ed8134 100644 --- a/packages/graphql/src/translate/utils/parse-mutable-field.ts +++ b/packages/graphql/src/translate/utils/parse-mutable-field.ts @@ -25,12 +25,12 @@ import { parseMutationField } from "../queryAST/factory/parsers/parse-mutation-f export function parseMutableField( nodeOrRel: Node | Relationship, key: string -): { settableField: MutableField; operator: MutationOperator } { +): { settableField: MutableField; operator: MutationOperator | undefined } { const { fieldName, operator } = parseMutationField(key); const field = nodeOrRel.mutableFields.find((x) => x.fieldName === fieldName); if (field) { - return { settableField: field, operator: operator ?? "SET" }; + return { settableField: field, operator: operator }; } throw new Error(`Transpile error: field ${key} not found`); diff --git a/packages/graphql/src/types/index.ts b/packages/graphql/src/types/index.ts index c492c11b99..a17718d7a4 100644 --- a/packages/graphql/src/types/index.ts +++ b/packages/graphql/src/types/index.ts @@ -28,6 +28,7 @@ import type { Neo4jGraphQLSubscriptionsCDCEngine } from "../classes/subscription import type { RelationshipNestedOperationsOption, RelationshipQueryDirectionOption } from "../constants"; import type { Neo4jGraphQLSchemaModel } from "../schema-model/Neo4jGraphQLSchemaModel"; import type { DefaultAnnotationValue } from "../schema-model/annotation/DefaultAnnotation"; +import type { FulltextField } from "../schema-model/annotation/FulltextAnnotation"; import type { VectorField } from "../schema-model/annotation/VectorAnnotation"; import type { RelationshipDirection } from "../schema-model/relationship/Relationship"; import type { JwtPayload } from "./jwt-payload"; @@ -44,11 +45,9 @@ export type AuthorizationContext = { }; export type FulltextContext = { - name: string | undefined; - fields: string[]; + index: FulltextField; + queryName: string; queryType: string; - queryName: string | undefined; - indexName: string | undefined; // TODO: not undefined once name is removed. scoreVariable: Cypher.Variable; }; @@ -232,7 +231,7 @@ export interface ConnectionQueryArgs { * Representation of the options arg * passed to resolvers. */ -export interface GraphQLOptionsArg { +export interface GraphQLSortingAndPaginationArgs { limit?: number | Integer; offset?: number | Integer; sort?: GraphQLSortArg[]; @@ -362,8 +361,6 @@ export type SubscriptionEngineContext = { export interface Neo4jGraphQLSubscriptionsEngine { events: EventEmitter; - publish(eventMeta: SubscriptionsEvent): Promise | void; - /** To be called, if needed, in getSchema */ init?(context: SubscriptionEngineContext): Promise; @@ -448,16 +445,16 @@ export type Neo4jFeaturesSettings = { * * NOTE: this will not remove user defined deprecated fields **/ + excludeDeprecatedFields?: { - implicitEqualFilters?: boolean; - implicitSet?: boolean; - deprecatedOptionsArgument?: boolean; - directedArgument?: boolean; - connectOrCreate?: boolean; - idAggregations?: boolean; - typename_IN?: boolean; + mutationOperations?: boolean; + aggregationFilters?: boolean; + relationshipFilters?: boolean; + attributeFilters?: boolean; }; vector?: Neo4jVectorSettings; + limitRequired?: boolean; + complexityEstimators?: boolean; }; /** Parsed features used in context */ diff --git a/packages/graphql/src/utils/execute.test.ts b/packages/graphql/src/utils/execute.test.ts index 1496f3166b..095eed8e6b 100644 --- a/packages/graphql/src/utils/execute.test.ts +++ b/packages/graphql/src/utils/execute.test.ts @@ -48,7 +48,7 @@ describe("execute", () => { const tx = { run: (paramCypher, paramParams) => { - expect(paramCypher).toEqual(cypher); + expect(paramCypher).toBe(`CYPHER 5\n${cypher}`); expect(paramParams).toEqual(params); return { records, summary: { counters: { updates: () => ({ test: 1 }) } } }; @@ -126,7 +126,7 @@ describe("execute", () => { const tx = { run: (paramCypher: string, paramParams) => { - expect(trimmer(paramCypher)).toEqual(cypher); + expect(trimmer(paramCypher)).toBe(`CYPHER 5 ${cypher}`); expect(paramParams).toEqual(params); return { records, summary: { counters: { updates: () => ({ test: 1 }) } } }; @@ -189,6 +189,7 @@ describe("execute", () => { `); const expectedCypher = trimmer(` + CYPHER 5 CYPHER runtime=interpreted planner=cost updateStrategy=default expressionEngine=compiled operatorEngine=compiled interpretedPipesFallback=all replan=default CREATE (u:User {title: $title}) RETURN u { .title } as u diff --git a/packages/graphql/src/utils/get-relationship-direction.ts b/packages/graphql/src/utils/get-relationship-direction.ts deleted file mode 100644 index cd7f1fcce3..0000000000 --- a/packages/graphql/src/utils/get-relationship-direction.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { RelationshipQueryDirectionOption } from "../constants"; -import type { RelationField } from "../types"; - -type QueryRelationshipDirection = "IN" | "OUT" | "undirected"; -type CypherRelationshipDirection = "left" | "right" | "undirected"; - -export function getCypherRelationshipDirection( - relationField: RelationField, - fieldArgs: { directed?: boolean } = {} -): CypherRelationshipDirection { - const direction = getRelationshipDirection(relationField, fieldArgs); - switch (direction) { - case "IN": - return "left"; - case "OUT": - return "right"; - case "undirected": - return "undirected"; - } -} - -function getRelationshipDirection( - relationField: RelationField, - fieldArgs: { directed?: boolean } -): QueryRelationshipDirection { - /** - * Duplicate of the schema-model `getCypherDirection` method; - **/ - if ( - fieldArgs.directed === false || - relationField.queryDirection === RelationshipQueryDirectionOption.UNDIRECTED_ONLY || - relationField.queryDirection === RelationshipQueryDirectionOption.UNDIRECTED - ) { - return "undirected"; - } - return relationField.direction; -} diff --git a/packages/graphql/src/utils/is-root-type.ts b/packages/graphql/src/utils/is-root-type.ts index 89228462fd..00505c173d 100644 --- a/packages/graphql/src/utils/is-root-type.ts +++ b/packages/graphql/src/utils/is-root-type.ts @@ -17,10 +17,10 @@ * limitations under the License. */ -import type { ObjectTypeDefinitionNode } from "graphql"; +import type { TypeDefinitionNode } from "graphql"; const rootTypes = ["Query", "Mutation", "Subscription"]; -export function isRootType(definition: ObjectTypeDefinitionNode): boolean { +export function isRootType(definition: TypeDefinitionNode): boolean { return rootTypes.includes(definition.name.value); } diff --git a/packages/graphql/tests/e2e/complexity-estimators.e2e.test.ts b/packages/graphql/tests/e2e/complexity-estimators.e2e.test.ts new file mode 100644 index 0000000000..dd7c2135d1 --- /dev/null +++ b/packages/graphql/tests/e2e/complexity-estimators.e2e.test.ts @@ -0,0 +1,211 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../utils/tests-helper"; +import type { TestGraphQLServer } from "./setup/apollo-server"; +import { ApolloTestServer } from "./setup/apollo-server"; +import { parse } from "graphql"; + +describe("limitRequired enabled", () => { + const testHelper = new TestHelper(); + + let server: TestGraphQLServer; + + beforeAll(async () => { + const typeDefs = ` + interface Production { + title: String + actors: [Actor!]! @declareRelationship + } + type Movie implements Production @node { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) + directors: [Actor!]! @relationship(type: "DIRECTED", direction: IN) + } + type Actor @node { + name: String + } + `; + + const neoSchema = await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { limitRequired: true, complexityEstimators: true }, + }); + + server = new ApolloTestServer( + neoSchema, + // eslint-disable-next-line @typescript-eslint/require-await + async ({ req }) => ({ + sessionConfig: { + database: testHelper.database, + }, + token: req.headers.authorization, + }), + true + ); + await server.start(); + }); + + afterAll(async () => { + await testHelper.close(); + await server.close(); + }); + + test("movies result", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + movies(limit: 9) { + title + } + } + `) + ); + expect(complexity).toBe(10); + }); + + test("movies with actors result", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + movies(limit: 5) { + title + actors(limit: 6) { + name + } + } + } + `) + ); + expect(complexity).toBe(13); + }); + + test("movies with actors and directors result", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + movies(limit: 5) { + title + actors(limit: 10) { + name + } + directors(limit: 4) { + name + } + } + } + `) + ); + expect(complexity).toBe(22); + }); + + test("productions with actors and directors result", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + productions(limit: 5) { + title + actors(limit: 10) { + name + } + } + } + `) + ); + expect(complexity).toBe(17); + }); +}); + +describe("limitRequired not enabled", () => { + const testHelper = new TestHelper(); + + let server: TestGraphQLServer; + + beforeAll(async () => { + const typeDefs = ` + interface Production { + title: String + actors: [Actor!]! @declareRelationship + } + type Movie implements Production @node { + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) + directors: [Actor!]! @relationship(type: "DIRECTED", direction: IN) + } + type Actor @node { + name: String + } + `; + + const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs, features: { complexityEstimators: true } }); + + server = new ApolloTestServer( + neoSchema, + // eslint-disable-next-line @typescript-eslint/require-await + async ({ req }) => ({ + sessionConfig: { + database: testHelper.database, + }, + token: req.headers.authorization, + }), + true + ); + await server.start(); + }); + + afterAll(async () => { + await testHelper.close(); + await server.close(); + }); + + test("movies with actors and directors result - no limit args", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + movies { + title + actors { + name + } + directors { + name + } + } + } + `) + ); + expect(complexity).toBe(6); + }); + + test("productions with actors and directors result - no limit args", async () => { + const complexity = await server.computeQueryComplexity( + parse(` + query { + productions { + title + actors { + name + } + } + } + `) + ); + expect(complexity).toBe(4); + }); +}); diff --git a/packages/graphql/tests/e2e/federation/apollo-federation-subgraph-compatibility/apollo-federation-subgraph-compatibility.e2e.test.ts b/packages/graphql/tests/e2e/federation/apollo-federation-subgraph-compatibility/apollo-federation-subgraph-compatibility.e2e.test.ts index d6b97c921f..7ba99fe763 100644 --- a/packages/graphql/tests/e2e/federation/apollo-federation-subgraph-compatibility/apollo-federation-subgraph-compatibility.e2e.test.ts +++ b/packages/graphql/tests/e2e/federation/apollo-federation-subgraph-compatibility/apollo-federation-subgraph-compatibility.e2e.test.ts @@ -17,16 +17,16 @@ * limitations under the License. */ +import { stripIgnoredCharacters } from "graphql"; import { gql } from "graphql-tag"; import { GatewayServer } from "../setup/gateway-server"; +import { Neo4j } from "../setup/neo4j"; import type { Server } from "../setup/server"; import { TestSubgraph } from "../setup/subgraph"; import { SubgraphServer } from "../setup/subgraph-server"; -import { Neo4j } from "../setup/neo4j"; import { schema as inventory } from "./subgraphs/inventory"; import { schema as users } from "./subgraphs/users"; import { graphqlRequest } from "./utils/client"; -import { stripIgnoredCharacters } from "graphql"; describe("Tests copied from https://github.com/apollographql/apollo-federation-subgraph-compatibility", () => { let inventoryServer: Server; @@ -62,11 +62,31 @@ describe("Tests copied from https://github.com/apollographql/apollo-federation-s id: ID! sku: String package: String - variation: ProductVariation @relationship(type: "HAS_VARIATION", direction: OUT) - dimensions: ProductDimension @relationship(type: "HAS_DIMENSIONS", direction: OUT) + variation: ProductVariation + @cypher( + statement: """ + MATCH (this)-[:HAS_VARIATION]->(res:ProductVariation) + RETURN res + """ + columnName: "res" + ) + dimensions: ProductDimension + @cypher( + statement: """ + MATCH (this)-[:HAS_DIMENSIONS]->(res:ProductDimension) + RETURN res + """ + columnName: "res" + ) createdBy: User @provides(fields: "totalProductsCreated") - @relationship(type: "CREATED_BY", direction: OUT) + @cypher( + statement: """ + MATCH (this)-[:CREATED_BY]->(res:User) + RETURN res + """ + columnName: "res" + ) notes: String @tag(name: "internal") research: [ProductResearch!]! @relationship(type: "HAS_RESEARCH", direction: OUT) } @@ -75,7 +95,7 @@ describe("Tests copied from https://github.com/apollographql/apollo-federation-s sku: String! package: String! reason: String - createdBy: User @relationship(type: "CREATED_BY", direction: OUT) + createdBy: [User!]! @relationship(type: "CREATED_BY", direction: OUT) } type ProductVariation @node { @@ -83,7 +103,14 @@ describe("Tests copied from https://github.com/apollographql/apollo-federation-s } type ProductResearch @key(fields: "study { caseNumber }") @node { - study: CaseStudy! @relationship(type: "HAS_STUDY", direction: OUT) + study: CaseStudy! + @cypher( + statement: """ + MATCH (this)-[:HAS_STUDY]->(res:CaseStudy) + RETURN res + """ + columnName: "res" + ) outcome: String } diff --git a/packages/graphql/tests/e2e/federation/authorization.e2e.test.ts b/packages/graphql/tests/e2e/federation/authorization.e2e.test.ts index 95be6991f9..9a2d87fe89 100644 --- a/packages/graphql/tests/e2e/federation/authorization.e2e.test.ts +++ b/packages/graphql/tests/e2e/federation/authorization.e2e.test.ts @@ -63,7 +63,7 @@ describe("Federation 2 Authorization", () => { type ${Review} @node { score: Int! description: String! - author: ${User}! @relationship(type: "AUTHORED", direction: IN) + author: [${User}!]! @relationship(type: "AUTHORED", direction: IN) } `; @@ -125,7 +125,9 @@ describe("Federation 2 Authorization", () => { expect(response.status).toBe(200); expect(response.body.data).toEqual({ - [Review.plural]: [{ description: "review", score: 5, author: { id: "1", name: null, password: null } }], + [Review.plural]: [ + { description: "review", score: 5, author: [{ id: "1", name: null, password: null }] }, + ], }); expect(response.body.errors[0].message).toBe("Forbidden"); }); @@ -155,7 +157,7 @@ describe("Federation 2 Authorization", () => { expect(response.status).toBe(200); expect(response.body.data).toEqual({ [Review.plural]: [ - { description: "review", score: 5, author: { id: "1", name: "user", password: "password" } }, + { description: "review", score: 5, author: [{ id: "1", name: "user", password: "password" }] }, ], }); expect(response.body.errors).toBeUndefined(); @@ -198,7 +200,7 @@ describe("Federation 2 Authorization", () => { type ${Review} @node { score: Int! description: String! - author: ${User}! @relationship(type: "AUTHORED", direction: IN) + author: [${User}!]! @relationship(type: "AUTHORED", direction: IN) } `; @@ -260,7 +262,7 @@ describe("Federation 2 Authorization", () => { expect(response.status).toBe(200); expect(response.body).toEqual({ data: { - [Review.plural]: [{ description: "review", score: 5, author: { id: "1", name: "user" } }], + [Review.plural]: [{ description: "review", score: 5, author: [{ id: "1", name: "user" }] }], }, }); }); @@ -289,7 +291,9 @@ describe("Federation 2 Authorization", () => { expect(response.status).toBe(200); expect(response.body.data).toEqual({ - [Review.plural]: [{ description: "review", score: 5, author: { id: "1", name: null, password: null } }], + [Review.plural]: [ + { description: "review", score: 5, author: [{ id: "1", name: null, password: null }] }, + ], }); expect(response.body.errors[0].message).toBe("Forbidden"); }); @@ -319,7 +323,7 @@ describe("Federation 2 Authorization", () => { expect(response.status).toBe(200); expect(response.body.data).toEqual({ [Review.plural]: [ - { description: "review", score: 5, author: { id: "1", name: "user", password: "password" } }, + { description: "review", score: 5, author: [{ id: "1", name: "user", password: "password" }] }, ], }); expect(response.body.errors).toBeUndefined(); diff --git a/packages/graphql/tests/e2e/federation/entities-basics.e2e.test.ts b/packages/graphql/tests/e2e/federation/entities-basics.e2e.test.ts index 8ca9ee6927..4ffacd10e0 100644 --- a/packages/graphql/tests/e2e/federation/entities-basics.e2e.test.ts +++ b/packages/graphql/tests/e2e/federation/entities-basics.e2e.test.ts @@ -61,7 +61,7 @@ describe("Federation 2 Entities Basics (https://www.apollographql.com/docs/feder type ${Review} @node { score: Int! description: String! - product: ${Product}! @relationship(type: "HAS_REVIEW", direction: IN) + product: [${Product}!]! @relationship(type: "HAS_REVIEW", direction: IN) } `; @@ -121,7 +121,9 @@ describe("Federation 2 Entities Basics (https://www.apollographql.com/docs/feder expect(response.status).toBe(200); expect(response.body).toEqual({ data: { - [Review.plural]: [{ description: "review", score: 5, product: { id: "1", price: 5, name: "product" } }], + [Review.plural]: [ + { description: "review", score: 5, product: [{ id: "1", price: 5, name: "product" }] }, + ], }, }); }); diff --git a/packages/graphql/tests/e2e/federation/quickstart.e2e.test.ts b/packages/graphql/tests/e2e/federation/quickstart.e2e.test.ts index 6293c67728..96e5d23d0b 100644 --- a/packages/graphql/tests/e2e/federation/quickstart.e2e.test.ts +++ b/packages/graphql/tests/e2e/federation/quickstart.e2e.test.ts @@ -73,7 +73,7 @@ describe("Federation 2 quickstart (https://www.apollographql.com/docs/federation "A number from 1 - 5 with 1 being lowest and 5 being highest" rating: Int "The location the review is about" - location: ${Location} @relationship(type: "HAS_REVIEW", direction: IN) + location: [${Location}!]! @relationship(type: "HAS_REVIEW", direction: IN) } `; diff --git a/packages/graphql/tests/e2e/neo4jgraphql/neo4j-database-info.test.ts b/packages/graphql/tests/e2e/neo4jgraphql/neo4j-database-info.test.ts index 8ddcb2b3e9..f65bc26dfe 100644 --- a/packages/graphql/tests/e2e/neo4jgraphql/neo4j-database-info.test.ts +++ b/packages/graphql/tests/e2e/neo4jgraphql/neo4j-database-info.test.ts @@ -89,8 +89,8 @@ describe("Create with specific neo4jDatabaseInfo set incorrectly", () => { let server: TestGraphQLServer; beforeAll(async () => { - const typeDefs = ` - type ${typeMovie} { + const typeDefs = /* GraphQL */ ` + type ${typeMovie} @node { title: String } `; diff --git a/packages/graphql/tests/e2e/setup/apollo-server.ts b/packages/graphql/tests/e2e/setup/apollo-server.ts index 3fd81a048e..0201ffc611 100644 --- a/packages/graphql/tests/e2e/setup/apollo-server.ts +++ b/packages/graphql/tests/e2e/setup/apollo-server.ts @@ -30,12 +30,16 @@ import { createServer } from "http"; import type { AddressInfo } from "ws"; import { WebSocketServer } from "ws"; import type { Neo4jGraphQL } from "../../../src"; +import { getComplexity } from "graphql-query-complexity"; +import { type DocumentNode } from "graphql"; +import { DefaultComplexityEstimators } from "../../../src/classes"; export interface TestGraphQLServer { path: string; wsPath: string; start(port?: number): Promise; close(): Promise; + computeQueryComplexity(query: DocumentNode): Promise; } type CustomContext = ExpressMiddlewareOptions["context"]; @@ -46,10 +50,12 @@ export class ApolloTestServer implements TestGraphQLServer { private _path?: string; private wsServer?: WebSocketServer; private customContext?: CustomContext; + private useEstimators: boolean; - constructor(schema: Neo4jGraphQL, customContext?: CustomContext) { + constructor(schema: Neo4jGraphQL, customContext?: CustomContext, useEstimators?: boolean) { this.schema = schema; this.customContext = customContext; + this.useEstimators = useEstimators ?? false; } public get path(): string { @@ -61,6 +67,18 @@ export class ApolloTestServer implements TestGraphQLServer { return this.path.replace("http://", "ws://"); } + public async computeQueryComplexity(query: DocumentNode): Promise { + const schema = await this.schema.getSchema(); + if (this.useEstimators) { + return getComplexity({ + schema, + query, + variables: {}, + estimators: DefaultComplexityEstimators, + }); + } + } + async start(): Promise { if (this.server) throw new Error(`Server already running on "${this.path}"`); const app = express(); @@ -72,6 +90,7 @@ export class ApolloTestServer implements TestGraphQLServer { this.server = httpServer; this.wsServer = wsServer; + const useEstimators = this.useEstimators; const schema = await this.schema.getSchema(); const serverCleanup = useServer( @@ -86,6 +105,29 @@ export class ApolloTestServer implements TestGraphQLServer { const server = new ApolloServer({ schema, plugins: [ + { + requestDidStart() { + return Promise.resolve({ + didResolveOperation({ request, document }) { + if (useEstimators) { + const complexity = getComplexity({ + schema, + query: document, + variables: request.variables, + estimators: DefaultComplexityEstimators, + }); + + if (complexity > 100) { + throw new Error( + `Query is too complex: ${complexity}. Maximum allowed complexity is 100.` + ); + } + } + return Promise.resolve(); + }, + }); + }, + }, ApolloServerPluginDrainHttpServer({ httpServer }), { serverWillStart() { diff --git a/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts index bad6b626a0..e386d5121f 100644 --- a/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/authorization/authentication.e2e.test.ts @@ -684,12 +684,12 @@ describe("Subscription authentication", () => { actors: [${typeActor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type ${typeActor} @authentication(operations: [READ]) @node { name: String! - id: Int @unique + id: Int movies: [${typeMovie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) } @@ -708,8 +708,8 @@ describe("Subscription authentication", () => { type ${typePerson} implements Reviewer @node { name: String! reputation: Int! @authentication(operations: [READ]) - id: Int @unique - reviewerId: Int @unique @authentication(operations: [READ]) + id: Int + reviewerId: Int @authentication(operations: [READ]) movies: [${typeMovie}!]! @relationship(type: "REVIEWED", direction: OUT, properties: "Review") } @@ -1008,7 +1008,7 @@ describe("Subscription authentication", () => { title_EQ: "Matrix", } update: { - title: "Matrix 2" + title_SET: "Matrix 2" } ) { ${typeMovie.plural} { @@ -1096,7 +1096,7 @@ describe("Subscription authentication", () => { title_EQ: "Matrix", } update: { - title: "Matrix 2" + title_SET: "Matrix 2" } ) { ${typeMovie.plural} { @@ -1184,7 +1184,7 @@ describe("Subscription authentication", () => { title_EQ: "Matrix", } update: { - title: "Matrix 2" + title_SET: "Matrix 2" } ) { ${typeMovie.plural} { @@ -1264,7 +1264,7 @@ describe("Subscription authentication", () => { title_EQ: "Matrix", } update: { - title: "Matrix 2" + title_SET: "Matrix 2" } ) { ${typeMovie.plural} { @@ -1340,7 +1340,7 @@ describe("Subscription authentication", () => { name_EQ: "Keanu" }, update: { - name: "Keanu Reeves" + name_SET: "Keanu Reeves" } ) { info { @@ -1977,7 +1977,7 @@ describe("Subscription authentication", () => { name_EQ: "Bob" } update: { - name: "John" + name_SET: "John" } ) { ${typePerson.plural} { @@ -2067,7 +2067,7 @@ describe("Subscription authentication", () => { name_EQ: "Bob" } update: { - name: "John" + name_SET: "John" } ) { ${typePerson.plural} { @@ -2158,7 +2158,7 @@ describe("Subscription authentication", () => { name_EQ: "Bob" } update: { - name: "John" + name_SET: "John" } ) { ${typePerson.plural} { @@ -2239,7 +2239,7 @@ describe("Subscription authentication", () => { url_EQ: "/bob" } update: { - url: "/john" + url_SET: "/john" } ) { ${typeInfluencer.plural} { diff --git a/packages/graphql/tests/e2e/subscriptions/authorization/filter/update.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/authorization/filter/update.e2e.test.ts index 6bc908f5c8..de461ffff2 100644 --- a/packages/graphql/tests/e2e/subscriptions/authorization/filter/update.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/authorization/filter/update.e2e.test.ts @@ -201,7 +201,7 @@ describe("Subscriptions authorization with update events", () => { .send({ query: ` mutation { - ${User.operations.update}(where: { id_EQ: "${id1}" }, update: { id: "${id2}" }) { + ${User.operations.update}(where: { id_EQ: "${id1}" }, update: { id_SET: "${id2}" }) { ${User.plural} { id } diff --git a/packages/graphql/tests/e2e/subscriptions/authorization/global-authentication.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/authorization/global-authentication.e2e.test.ts index 7dcf329247..2c822ec9d8 100644 --- a/packages/graphql/tests/e2e/subscriptions/authorization/global-authentication.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/authorization/global-authentication.e2e.test.ts @@ -31,7 +31,7 @@ describe("Subscription global authentication", () => { const secret = "secret"; const typeMovie = testHelper.createUniqueType("Movie"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String! } @@ -77,7 +77,7 @@ describe("Subscription global authentication", () => { test("global authentication for wsClient", async () => { wsClient = new WebSocketTestClient(server.wsPath); - await wsClient.subscribe(` + await wsClient.subscribe(/* GraphQL */ ` subscription { ${typeMovie.operations.subscribe.created} { ${typeMovie.operations.subscribe.payload.created} { @@ -97,7 +97,7 @@ describe("Subscription global authentication", () => { test("global authentication for supertest client", async () => { wsClient = new WebSocketTestClient(server.wsPath, jwtToken); - await wsClient.subscribe(` + await wsClient.subscribe(/* GraphQL */ ` subscription { ${typeMovie.operations.subscribe.created} { ${typeMovie.operations.subscribe.payload.created} { @@ -149,7 +149,7 @@ describe("Subscription global authentication", () => { test("global authentication for wsClient", async () => { wsClient = new WebSocketTestClient(server.wsPath, "Bearer xxx.invalidtoken.xxx"); - await wsClient.subscribe(` + await wsClient.subscribe(/* GraphQL */ ` subscription { ${typeMovie.operations.subscribe.created} { ${typeMovie.operations.subscribe.payload.created} { @@ -169,7 +169,7 @@ describe("Subscription global authentication", () => { test("global authentication for supertest client", async () => { wsClient = new WebSocketTestClient(server.wsPath, jwtToken); - await wsClient.subscribe(` + await wsClient.subscribe(/* GraphQL */ ` subscription { ${typeMovie.operations.subscribe.created} { ${typeMovie.operations.subscribe.payload.created} { @@ -221,7 +221,7 @@ describe("Subscription global authentication", () => { test("global authentication wsClient and supertest client", async () => { wsClient = new WebSocketTestClient(server.wsPath, jwtToken); - await wsClient.subscribe(` + await wsClient.subscribe(/* GraphQL */ ` subscription { ${typeMovie.operations.subscribe.created} { ${typeMovie.operations.subscribe.payload.created} { @@ -257,7 +257,7 @@ describe("Subscription global authentication", () => { .post("") .set("authorization", clientJwtToken) .send({ - query: ` + query: /* GraphQL */ ` mutation { ${typeMovie.operations.create}(input: [{ title: "${title}" }]) { ${typeMovie.plural} { diff --git a/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts index ecabaee6bc..453a078250 100644 --- a/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/create.e2e.test.ts @@ -39,10 +39,10 @@ describe("Create Subscription", () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - actors: [${typeActor}] + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) } type ${typeActor} @subscription(events: []) @node { name: String diff --git a/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts index 3a708e3df6..d431783a2b 100644 --- a/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/delete-complex.e2e.test.ts @@ -45,7 +45,7 @@ describe("Delete Subscriptions - with interfaces, unions and nested operations", typePerson = testHelper.createUniqueType("Person"); typeInfluencer = testHelper.createUniqueType("Influencer"); - typeDefs = ` + typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String! actors: [${typeActor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) diff --git a/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts index b54ce06845..396d0cbe32 100644 --- a/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/delete.e2e.test.ts @@ -39,10 +39,10 @@ describe("$name Delete Subscription", () => { beforeEach(async () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - actors: [${typeActor}] + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) } type ${typeActor} @subscription(events: []) @node { name: String diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts index 25dbb0d619..9b2a65cfaa 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts index 3cbbf30ab0..8d2d121640 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Create Subscription with filters valid of number types (Int, Float, Bi type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts index 8284c87140..d9709dca17 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Create Subscription with filters valid on string types (String, ID)", type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts index 89f218c513..c1418b5f5b 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/create.e2e.test.ts @@ -46,7 +46,7 @@ describe("Create Subscription with optional filters valid for all types", () => averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts index 1fd34f0da2..c4d56cf6d9 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts index 9505e41b6e..4f890631c9 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Delete Subscription", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts index 2ce138bf73..8dc3cf1b98 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Delete Subscription", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts index 6e8bde45dc..220602813c 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/delete.e2e.test.ts @@ -46,7 +46,7 @@ describe("Delete Subscription", () => { averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } `; diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts index 1c54748001..b41b5598f4 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-array-filters.e2e.test.ts @@ -40,15 +40,15 @@ describe("Create Subscription with optional filters valid for all types", () => const typeDefs = ` type ${typeMovie} @node { id: ID - similarIds: [ID] + similarIds: [ID!] title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int - allDates: [Int] + allDates: [Int!] averageRating: Float - allRatings: [Float] + allRatings: [Float!] fileSize: BigInt - allSizes: [BigInt] + allSizes: [BigInt!] isFavorite: Boolean } `; @@ -369,8 +369,8 @@ describe("Create Subscription with optional filters valid for all types", () => query: ` mutation { ${typeMovie.operations.update}(where: { ${fieldName}_EQ: ${makeTypedFieldValue( - oldValue - )} }, update: { ${fieldName}: ${makeTypedFieldValue(newValue)} }) { + oldValue + )} }, update: { ${fieldName}_SET: ${makeTypedFieldValue(newValue)} }) { ${typeMovie.plural} { title releasedIn diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts index 7ef5fc7c1b..933f667762 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-number-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Update Subscriptions", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt @@ -894,8 +894,8 @@ describe("Update Subscriptions", () => { query: ` mutation { ${typeMovie.operations.update}(where: { ${fieldName}_EQ: ${makeTypedFieldValue( - oldValue - )} }, update: { ${fieldName}: ${makeTypedFieldValue(newValue)} }) { + oldValue + )} }, update: { ${fieldName}_SET: ${makeTypedFieldValue(newValue)} }) { ${typeMovie.plural} { title releasedIn diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts index 213b4326a4..385eaf6dd6 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update-string-filters.e2e.test.ts @@ -41,7 +41,7 @@ describe("Update Subscriptions", () => { type ${typeMovie} @node { id: ID title: String - similarTitles: [String] + similarTitles: [String!] releasedIn: Int averageRating: Float fileSize: BigInt @@ -629,8 +629,8 @@ describe("Update Subscriptions", () => { query: ` mutation { ${typeMovie.operations.update}(where: { ${fieldName}_EQ: ${makeTypedFieldValue( - oldValue - )} }, update: { ${fieldName}: ${makeTypedFieldValue(newValue)} }) { + oldValue + )} }, update: { ${fieldName}_SET: ${makeTypedFieldValue(newValue)} }) { ${typeMovie.plural} { id title diff --git a/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts index a55784da4f..de34714ace 100644 --- a/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/filtering/update.e2e.test.ts @@ -48,7 +48,7 @@ describe("Update Subscriptions", () => { averageRating: Float fileSize: BigInt isFavorite: Boolean - similarTitles: [String] + similarTitles: [String!] } type ${typeActor} @node { @@ -884,8 +884,8 @@ describe("Update Subscriptions", () => { query: ` mutation { ${typeMovie.operations.update}(where: { ${fieldName}_EQ: ${makeTypedFieldValue( - oldValue - )} }, update: { ${fieldName}: ${makeTypedFieldValue(newValue)} }) { + oldValue + )} }, update: { ${fieldName}_SET: ${makeTypedFieldValue(newValue)} }) { ${typeMovie.plural} { title releasedIn diff --git a/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts index 9e5c465f89..976a2ecbdc 100644 --- a/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/issues/3698.e2e.test.ts @@ -45,19 +45,19 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { id: String! title: String! - genre: ${typeGenre}! + genre: [${typeGenre}!]! info: String! } type ${typeMovie} implements IProduct @node { id: String! title: String! - genre: ${typeGenre}! @relationship(type: "HAS_GENRE", direction: OUT) + genre: [${typeGenre}!]! @relationship(type: "HAS_GENRE", direction: OUT) info: String! @customResolver(requires: "id title") } type ${typeGenre} @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } diff --git a/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts index 8cd1fbee23..9f2565faf9 100644 --- a/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/issues/5586.e2e.test.ts @@ -39,8 +39,8 @@ describe("https://github.com/neo4j/graphql/issues/5586", () => { Entity = testHelper.createUniqueType("Entity"); typeDefs = /* GraphQL */ ` - type ${Entity} { - id: ID! @id @unique + type ${Entity} @node { + id: ID! @id name: String } `; @@ -97,7 +97,7 @@ describe("https://github.com/neo4j/graphql/issues/5586", () => { .send({ query: /* GraphQL */ ` mutation { - ${Entity.operations.update}(update: { name: "new" }) { + ${Entity.operations.update}(update: { name_SET: "new" }) { ${Entity.plural} { name id diff --git a/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts b/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts index 31362268f3..d41fb36360 100644 --- a/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts +++ b/packages/graphql/tests/e2e/subscriptions/update.e2e.test.ts @@ -39,15 +39,14 @@ describe("Delete Subscription", () => { beforeEach(async () => { typeMovie = testHelper.createUniqueType("Movie"); typeActor = testHelper.createUniqueType("Actor"); - const typeDefs = ` - type ${typeMovie} @node { + const typeDefs = /* GraphQL */ ` + type ${typeMovie} @node { title: String - actors: [${typeActor}] - } - type ${typeActor} @subscription(events: []) @node { - name: String - } - `; + actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + type ${typeActor} @subscription(events: []) @node { + name: String + }`; const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs, @@ -236,7 +235,7 @@ describe("Delete Subscription", () => { .send({ query: ` mutation { - ${typeMovie.operations.update}(where: { title_EQ: "${oldTitle}" }, update: { title: "${newTitle}" }) { + ${typeMovie.operations.update}(where: { title_EQ: "${oldTitle}" }, update: { title_SET: "${newTitle}" }) { ${typeMovie.plural} { title } @@ -253,7 +252,7 @@ describe("Delete Subscription", () => { .send({ query: ` mutation { - ${typeActor.operations.update}(where: { name_EQ: "${oldName}" }, update: { name: "${newName}" }) { + ${typeActor.operations.update}(where: { name_EQ: "${oldName}" }, update: { name_SET: "${newName}" }) { ${typeActor.plural} { name } diff --git a/packages/graphql/tests/integration/aggregations/top-level/alias.int.test.ts b/packages/graphql/tests/integration/aggregations/top-level/alias.int.test.ts index a8367a9959..d22ba73ef5 100644 --- a/packages/graphql/tests/integration/aggregations/top-level/alias.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/top-level/alias.int.test.ts @@ -69,10 +69,6 @@ describe("aggregations-top_level-alias", () => { { ${typeMovie.operations.aggregate}(where: { testString_EQ: "${testString}" }) { _count: count - _id: id { - _shortest: shortest - _longest: longest - } _title: title { _shortest: shortest _longest: longest @@ -96,10 +92,6 @@ describe("aggregations-top_level-alias", () => { expect((gqlResult.data as any)[typeMovie.operations.aggregate]).toEqual({ _count: 4, - _id: { - _shortest: "1", - _longest: "4444", - }, _title: { _shortest: "1", _longest: "4444", diff --git a/packages/graphql/tests/integration/aggregations/top-level/authorization.int.test.ts b/packages/graphql/tests/integration/aggregations/top-level/authorization.int.test.ts index 3b0c6108f7..8caa5ae18c 100644 --- a/packages/graphql/tests/integration/aggregations/top-level/authorization.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/top-level/authorization.int.test.ts @@ -81,11 +81,13 @@ describe("aggregations-top_level authorization", () => { type Post @node { content: String - creator: User! @relationship(type: "POSTED", direction: IN) + creator: [User!]! @relationship(type: "POSTED", direction: IN) } extend type Post - @authorization(filter: [{ operations: [AGGREGATE], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + filter: [{ operations: [AGGREGATE], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) `; const userId = generate({ @@ -130,9 +132,11 @@ describe("aggregations-top_level authorization", () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) imdbRatingInt: Int - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { @@ -179,66 +183,16 @@ describe("aggregations-top_level authorization", () => { expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); }); - test("should throw when invalid allow when aggregating a ID field", async () => { - const typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) - someId: ID - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) - } - - type Person @node { - id: ID - } - `; - - const movieId = generate({ - charset: "alphabetic", - }); - - const userId = generate({ - charset: "alphabetic", - }); - - const query = ` - { - moviesAggregate(where: {id_EQ: "${movieId}"}) { - someId { - shortest - longest - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - await testHelper.executeCypher(` - CREATE (:Person {id: "${userId}"})-[:DIRECTED]->(:Movie {id: "${movieId}", someId: "some-random-string"}) - `); - - const token = createBearerToken(secret, { sub: "invalid" }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); test("should throw when invalid allow when aggregating a String field", async () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) someString: String - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { @@ -289,9 +243,11 @@ describe("aggregations-top_level authorization", () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) imdbRatingFloat: Float - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { @@ -342,9 +298,11 @@ describe("aggregations-top_level authorization", () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) imdbRatingBigInt: BigInt - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { @@ -395,9 +353,11 @@ describe("aggregations-top_level authorization", () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) createdAt: DateTime - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { @@ -448,9 +408,11 @@ describe("aggregations-top_level authorization", () => { const typeDefs = /* GraphQL */ ` type Movie @node { id: ID - director: Person! @relationship(type: "DIRECTED", direction: IN) + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) screenTime: Duration - @authorization(validate: [{ when: BEFORE, where: { node: { director: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { director_SINGLE: { id_EQ: "$jwt.sub" } } } }] + ) } type Person @node { diff --git a/packages/graphql/tests/integration/aggregations/top-level/id.int.test.ts b/packages/graphql/tests/integration/aggregations/top-level/id.int.test.ts deleted file mode 100644 index c7c2fe3063..0000000000 --- a/packages/graphql/tests/integration/aggregations/top-level/id.int.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import type { UniqueType } from "../../../utils/graphql-types"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("aggregations-top_level-id", () => { - const testHelper = new TestHelper(); - let Movie: UniqueType; - - beforeEach(async () => { - Movie = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${Movie} @node { - testId: ID - id: ID - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should return the shortest of node properties", async () => { - const id = generate({ - charset: "alphabetic", - readable: true, - }); - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {testId: $id, id: "1"}) - CREATE (:${Movie} {testId: $id, id: "22"}) - CREATE (:${Movie} {testId: $id, id: "333"}) - CREATE (:${Movie} {testId: $id, id: "4444"}) - `, - { - id, - } - ); - - const query = ` - { - ${Movie.operations.aggregate}(where: {testId_EQ: "${id}"}) { - id { - shortest - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult.data as any)[Movie.operations.aggregate]).toEqual({ - id: { - shortest: "1", - }, - }); - }); - - test("should return the longest of node properties", async () => { - const id = generate({ - charset: "alphabetic", - readable: true, - }); - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {testId: $id, id: "1"}) - CREATE (:${Movie} {testId: $id, id: "22"}) - CREATE (:${Movie} {testId: $id, id: "333"}) - CREATE (:${Movie} {testId: $id, id: "4444"}) - `, - { - id, - } - ); - - const query = ` - { - ${Movie.operations.aggregate}(where: {testId_EQ: "${id}"}) { - id { - longest - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult.data as any)[Movie.operations.aggregate]).toEqual({ - id: { - longest: "4444", - }, - }); - }); - - test("should return the shortest and longest of node properties", async () => { - const id = generate({ - charset: "alphabetic", - readable: true, - }); - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {testId: $id, id: "1"}) - CREATE (:${Movie} {testId: $id, id: "22"}) - CREATE (:${Movie} {testId: $id, id: "333"}) - CREATE (:${Movie} {testId: $id, id: "4444"}) - `, - { - id, - } - ); - - const query = ` - { - ${Movie.operations.aggregate}(where: {testId_EQ: "${id}"}) { - id { - shortest - longest - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult.data as any)[Movie.operations.aggregate]).toEqual({ - id: { - shortest: "1", - longest: "4444", - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/aggregations/top-level/many.int.test.ts b/packages/graphql/tests/integration/aggregations/top-level/many.int.test.ts index 547b379cde..66509be954 100644 --- a/packages/graphql/tests/integration/aggregations/top-level/many.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/top-level/many.int.test.ts @@ -68,10 +68,6 @@ describe("aggregations-top_level-many", () => { const query = ` { ${typeMovie.operations.aggregate}(where: { testId_EQ: "${testId}" }) { - id { - shortest - longest - } title { shortest longest @@ -98,10 +94,6 @@ describe("aggregations-top_level-many", () => { expect(gqlResult.errors).toBeUndefined(); expect((gqlResult.data as any)[typeMovie.operations.aggregate]).toEqual({ - id: { - shortest: "1", - longest: "4444", - }, title: { shortest: "1", longest: "4444", diff --git a/packages/graphql/tests/integration/aggregations/where/AND-OR-operations.int.test.ts b/packages/graphql/tests/integration/aggregations/where/AND-OR-operations.int.test.ts index d74146234d..4925f4df03 100644 --- a/packages/graphql/tests/integration/aggregations/where/AND-OR-operations.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/AND-OR-operations.int.test.ts @@ -79,9 +79,9 @@ describe("Nested within AND/OR", () => { query { ${postType.plural}(where: { likesAggregate: { - count_EQ: 3 + count: { eq: 3 } node: { - testString_SHORTEST_LENGTH_EQUAL: 3 + testString: { shortestLength: { eq: 3 } } } } }) { @@ -108,10 +108,10 @@ describe("Nested within AND/OR", () => { ${postType.plural}(where: { likesAggregate: { OR: [ - { count_EQ: 3 } + { count: {eq: 3 } } { node: { - testString_SHORTEST_LENGTH_EQUAL: 3 + testString: { shortestLength: { eq: 3 } } } } ] @@ -149,10 +149,10 @@ describe("Nested within AND/OR", () => { ${postType.plural}(where: { likesAggregate: { AND: [ - { count_EQ: 3 } + { count: { eq: 3 } } { node: { - testString_SHORTEST_LENGTH_EQUAL: 3 + testString: {shortestLength: {eq: 3} } } } ] @@ -181,17 +181,17 @@ describe("Nested within AND/OR", () => { ${postType.plural}(where: { likesAggregate: { AND: [ - { count_LTE: 2 } + { count: { lte: 2 } } { AND: [ { node: { - testString_SHORTEST_LENGTH_LT: 4 + testString: {shortestLength: {lt: 4} } } } { node: { - testString_SHORTEST_LENGTH_GT: 2 + testString: {shortestLength: { gt: 2 } } } } ] @@ -230,12 +230,12 @@ describe("Nested within AND/OR", () => { AND: [ { node: { - NOT: { testString_SHORTEST_LENGTH_GT: 4 } + NOT: { testString: {shortestLength: { gt: 4 } } } } } { node: { - testString_SHORTEST_LENGTH_GT: 2 + testString: {shortestLength: { gt: 2 } } } } ] @@ -269,17 +269,17 @@ describe("Nested within AND/OR", () => { ${postType.plural}(where: { likesAggregate: { OR: [ - { count_LTE: 2 } + { count: { lte: 2 } } { OR: [ { node: { - testString_SHORTEST_LENGTH_LT: 4 + testString: {shortestLength: {lt: 4} } } } { node: { - testString_SHORTEST_LENGTH_GT: 20 + testString: {shortestLength: { gt: 20 } } } } ] @@ -319,17 +319,17 @@ describe("Nested within AND/OR", () => { ${postType.plural}(where: { likesAggregate: { AND: [ - { count_LTE: 2 } + { count: { lte: 2 } } { OR: [ { node: { - testString_SHORTEST_LENGTH_LT: 4 + testString: {shortestLength: {lt: 4} } } } { node: { - testString_SHORTEST_LENGTH_GT: 20 + testString: {shortestLength: { gt: 20 } } } } ] @@ -368,12 +368,12 @@ describe("Nested within AND/OR", () => { OR: [ { node: { - NOT: { testString_SHORTEST_LENGTH_GT: 4 } + NOT: { testString: {shortestLength: { gt: 4 } } } } } { node: { - testString_SHORTEST_LENGTH_GT: 20 + testString: {shortestLength: { gt: 20 } } } } ] @@ -412,12 +412,12 @@ describe("Nested within AND/OR", () => { AND: [ { node: { - testString_SHORTEST_LENGTH_LT: 4 + testString: {shortestLength: {lt: 4} } } } { node: { - testString_SHORTEST_LENGTH_GT: 2 + testString: {shortestLength: { gt: 2 } } } } ] diff --git a/packages/graphql/tests/integration/aggregations/where/count.int.test.ts b/packages/graphql/tests/integration/aggregations/where/count.int.test.ts index 9e38469720..31ea26d993 100644 --- a/packages/graphql/tests/integration/aggregations/where/count.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/count.int.test.ts @@ -62,7 +62,7 @@ describe("aggregations-where-count", () => { const query = /* GraphQL */ ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_EQ: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: {eq: 1 } } }) { testString likes { testString @@ -102,7 +102,7 @@ describe("aggregations-where-count", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LT: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { lt: 1 } } }) { testString likes { testString @@ -142,7 +142,7 @@ describe("aggregations-where-count", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LTE: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { lte: 1 } } }) { testString likes { testString @@ -187,7 +187,7 @@ describe("aggregations-where-count", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GT: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { gt: 1 } } }) { testString likes { testString @@ -227,7 +227,7 @@ describe("aggregations-where-count", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GTE: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { gte: 1 } } }) { testString likes { testString @@ -305,7 +305,7 @@ describe("aggregations-where-count interface relationships of concrete types", const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_EQ: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: {eq: 1 } } }) { testString likes { testString @@ -345,7 +345,7 @@ describe("aggregations-where-count interface relationships of concrete types", const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LT: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { lt: 1 } } }) { testString likes { testString @@ -385,7 +385,7 @@ describe("aggregations-where-count interface relationships of concrete types", const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LTE: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { lte: 1 } } }) { testString likes { testString @@ -430,7 +430,7 @@ describe("aggregations-where-count interface relationships of concrete types", const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GT: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { gt: 1 } } }) { testString likes { testString @@ -470,7 +470,7 @@ describe("aggregations-where-count interface relationships of concrete types", const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GTE: 1 } }) { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count: { gte: 1 } } }) { testString likes { testString diff --git a/packages/graphql/tests/integration/aggregations/where/edge/int.int.test.ts b/packages/graphql/tests/integration/aggregations/where/edge/int.int.test.ts index 95d3b907a3..c6cec2d414 100644 --- a/packages/graphql/tests/integration/aggregations/where/edge/int.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/edge/int.int.test.ts @@ -76,7 +76,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: { average: { eq: ${avg} } } } } }) { testString likes { testString @@ -115,7 +115,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: {average: {gt: ${avgGT} } } } } }) { testString likes { testString @@ -153,7 +153,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: {average: {gte: ${avg} } } } } }) { testString likes { testString @@ -191,7 +191,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: {average: {lt: ${avgLT} } } } } }) { testString likes { testString @@ -229,7 +229,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: {average: {lte: ${avg} } } } } }) { testString likes { testString @@ -273,7 +273,7 @@ describe("aggregations-where-edge-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_SUM_EQUAL: ${sum} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { someInt: {sum: {eq: ${sum} } } } } }) { testString likes { testString diff --git a/packages/graphql/tests/integration/aggregations/where/edge/string.int.test.ts b/packages/graphql/tests/integration/aggregations/where/edge/string.int.test.ts index 1cc17f1320..ff67c25a6f 100644 --- a/packages/graphql/tests/integration/aggregations/where/edge/string.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/edge/string.int.test.ts @@ -86,7 +86,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { shortestLength: { eq: ${shortestTestString.length} } } } } }) { testString likes { testString @@ -145,7 +145,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { longestLength: {eq: ${longestTestString.length} } } } }}) { testString likes { testString @@ -209,7 +209,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: {eq: ${avg} } } } } }) { testString likes { testString @@ -270,7 +270,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { gt: ${avgGT} } } } } }) { testString likes { testString @@ -330,7 +330,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: {averageLength: { gte: ${avg} } } } } }) { testString likes { testString @@ -391,7 +391,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -451,7 +451,7 @@ describe("aggregations-where-edge-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { lte: ${avg} } } } } }) { testString likes { testString @@ -550,7 +550,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { testString likes { testString @@ -609,7 +609,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { testString likes { testString @@ -673,7 +673,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { eq: ${avg} } } } } }) { testString likes { testString @@ -734,7 +734,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { gt: ${avgGT} } } } } }) { testString likes { testString @@ -794,7 +794,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { gte: ${avg} } } } } }) { testString likes { testString @@ -855,7 +855,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -915,7 +915,7 @@ describe("aggregations-where-edge-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { edge: { testString: { averageLength: { lte: ${avg} } } } } }) { testString likes { testString diff --git a/packages/graphql/tests/integration/aggregations/where/node/int-connections.int.test.ts b/packages/graphql/tests/integration/aggregations/where/node/int-connections.int.test.ts index 3ba3b497d9..c95e49df22 100644 --- a/packages/graphql/tests/integration/aggregations/where/node/int-connections.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/node/int-connections.int.test.ts @@ -73,7 +73,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { eq: ${avg} } } } } }) { edges { node { testString @@ -117,7 +117,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { gt: ${avgGT} } } } } }) { edges { node { testString @@ -160,7 +160,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { gte: ${avg} } } } } }) { edges { node { testString @@ -204,7 +204,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { lt: ${avgLT} } } } } }) { edges { node { testString @@ -247,7 +247,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { lte: ${avg} } } } } }) { edges { node { testString @@ -296,7 +296,7 @@ describe("aggregations-where-node-int - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { sum: {eq: ${sum} } } } } }) { edges { node { testString @@ -385,7 +385,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { eq: ${avg} } } } } }) { edges { node { testString @@ -429,7 +429,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { gt: ${avgGT} } } } } }) { edges { node { testString @@ -472,7 +472,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { gte: ${avg} } } } } }) { edges { node { testString @@ -516,7 +516,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { lt: ${avgLT} } } } } }) { edges { node { testString @@ -559,7 +559,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { lte: ${avg} } } } } }) { edges { node { testString @@ -608,7 +608,7 @@ describe("aggregations-where-node-int - connections - interface relationships of const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + ${Post.operations.connection}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { sum: {eq: ${sum} } } } } }) { edges { node { testString diff --git a/packages/graphql/tests/integration/aggregations/where/node/int.int.test.ts b/packages/graphql/tests/integration/aggregations/where/node/int.int.test.ts index fb7b3d20e4..22c815d56a 100644 --- a/packages/graphql/tests/integration/aggregations/where/node/int.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/node/int.int.test.ts @@ -85,7 +85,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}" }, likesAggregate: { node: { someInt: { average: {eq: ${avg} } } } } }) { testString likes { testString @@ -124,7 +124,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { someInt: {average: { gt: ${avgGT} } } } } }) { testString likes { testString @@ -163,7 +163,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: {gte: ${avg} } } } } }) { testString likes { testString @@ -203,7 +203,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: {average: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -242,7 +242,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: {average: { lte: ${avg} } } } } }) { testString likes { testString @@ -286,7 +286,7 @@ describe("aggregations-where-node-int", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + ${Post.plural}(where: { testString: {eq:"${testString}"}, likesAggregate: { node: { someInt: {sum: {eq: ${sum} } } } } }) { testString likes { testString @@ -370,7 +370,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: {eq: ${avg} } } } } }) { testString likes { testString @@ -410,7 +410,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}" }, likesAggregate: { node: { someInt: {average: {gt: ${avgGT} } } } } }) { testString likes { testString @@ -449,7 +449,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}" }, likesAggregate: { node: { someInt: {average: {gte: ${avg} } } } } }) { testString likes { testString @@ -489,7 +489,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: { average: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -528,7 +528,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: {average: {lte: ${avg} } } } } }) { testString likes { testString @@ -573,7 +573,7 @@ describe("aggregations-where-node-int interface relationships of concrete types" const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + ${Post.plural}(where: { testString: {eq: "${testString}"}, likesAggregate: { node: { someInt: {sum: { eq: ${sum} } } } } }) { testString likes { testString diff --git a/packages/graphql/tests/integration/aggregations/where/node/string-connections.int.test.ts b/packages/graphql/tests/integration/aggregations/where/node/string-connections.int.test.ts index 11962c0a34..ff6757dbf8 100644 --- a/packages/graphql/tests/integration/aggregations/where/node/string-connections.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/node/string-connections.int.test.ts @@ -82,7 +82,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { shortestLength: { eq: ${shortestTestString.length} } } } } }) { edges { node { testString @@ -149,7 +149,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { longestLength: { eq: ${longestTestString.length} } } } } }) { edges { node { testString @@ -222,7 +222,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { eq: ${avg} } } } } }) { edges { node { testString @@ -288,7 +288,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { gt: ${avgGT} } } } } }) { edges { node { testString @@ -353,7 +353,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { gte: ${avg} } } } } }) { edges { node { testString @@ -419,7 +419,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { lt: ${avgLT} } } } } }) { edges { node { testString @@ -484,7 +484,7 @@ describe("aggregations-where-node-string - connections", () => { const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { lte: ${avg} } } } } }) { edges { node { testString @@ -583,7 +583,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { shortestLength: { eq: ${shortestTestString.length} } } } } }) { edges { node { testString @@ -650,7 +650,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { longestLength: { eq: ${longestTestString.length} } } } } }) { edges { node { testString @@ -723,7 +723,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { eq: ${avg} } } } } }) { edges { node { testString @@ -789,7 +789,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { gt: ${avgGT} } } } } }) { edges { node { testString @@ -854,7 +854,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { gte: ${avg} } } } } }) { edges { node { testString @@ -920,7 +920,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { lt: ${avgLT} } } } } }) { edges { node { testString @@ -985,7 +985,7 @@ describe("aggregations-where-node-string - connections - interface relationships const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { averageLength: { lte: ${avg} } } } } }) { edges { node { testString @@ -1089,7 +1089,7 @@ describe("aggregations-where-node-string - connections - relationships of interf const query = ` { - ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.operations.connection}(where: { testString: { eq: "${testString}" }, likesAggregate: { node: { testString: { shortestLength: { eq: ${shortestTestString.length} } } } } }) { edges { node { testString diff --git a/packages/graphql/tests/integration/aggregations/where/node/string.int.test.ts b/packages/graphql/tests/integration/aggregations/where/node/string.int.test.ts index 8ba9d1a565..188f53904a 100644 --- a/packages/graphql/tests/integration/aggregations/where/node/string.int.test.ts +++ b/packages/graphql/tests/integration/aggregations/where/node/string.int.test.ts @@ -82,7 +82,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: {shortestLength: { eq: ${shortestTestString.length} } } } } }) { testString likes { testString @@ -141,7 +141,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { longestLength: { eq: ${longestTestString.length} } } } } }) { testString likes { testString @@ -205,7 +205,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { eq: ${avg} } } } } }) { testString likes { testString @@ -266,7 +266,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { gt: ${avgGT} } } } } }) { testString likes { testString @@ -327,7 +327,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { gte: ${avg} } } } } }) { testString likes { testString @@ -389,7 +389,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -450,7 +450,7 @@ describe("aggregations-where-node-string", () => { const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { lte: ${avg} } } } } }) { testString likes { testString @@ -545,7 +545,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: {shortestLength: { eq: ${shortestTestString.length} } } } } }) { testString likes { testString @@ -606,7 +606,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { longestLength: { eq: ${longestTestString.length} } } } } }) { testString likes { testString @@ -672,7 +672,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { eq: ${avg} } } } } }) { testString likes { testString @@ -734,7 +734,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { gt: ${avgGT} } } } } }) { testString likes { testString @@ -795,7 +795,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { gte: ${avg} } } } } }) { testString likes { testString @@ -857,7 +857,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { lt: ${avgLT} } } } } }) { testString likes { testString @@ -918,7 +918,7 @@ describe("aggregations-where-node-string interface relationships of concrete typ const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: { averageLength: { lte: ${avg} } } } } }) { testString likes { testString @@ -1018,7 +1018,7 @@ describe("aggregations-where-node-string relationships of interface types", () = const query = ` { - ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + ${Post.plural}(where: { testString: { eq: "${testString}"}, likesAggregate: { node: { testString: {shortestLength: { eq: ${shortestTestString.length} } } } } }) { testString likes { testString diff --git a/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts index bb01621134..2e4ef1e94a 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-and-push-errors.int.test.ts @@ -39,8 +39,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -83,8 +83,8 @@ describe("array-pop-and-push", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) - moreTags: [String] + tags: [String!] @authentication(operations: [UPDATE]) + moreTags: [String!] } `; @@ -131,8 +131,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts index 2744e9270d..6c1b66d3e0 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-and-push.int.test.ts @@ -36,8 +36,8 @@ describe("array-pop-and-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -49,7 +49,7 @@ describe("array-pop-and-push", () => { const update = ` mutation { - ${typeMovie.operations.update} (update: { tags_PUSH: "new tag", moreTags_POP: 2 }) { + ${typeMovie.operations.update} (update: { tags: {push: "new tag" }, moreTags: {pop: 2 } }) { ${typeMovie.plural} { title tags diff --git a/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts index 074f8d7459..7a74a84cbc 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop-errors.int.test.ts @@ -39,7 +39,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -83,8 +83,8 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - otherTags: [String] + tags: [String!] + otherTags: [String!] } `; @@ -130,7 +130,7 @@ describe("array-pop-errors", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) + tags: [String!] @authentication(operations: [UPDATE]) } `; @@ -179,7 +179,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -224,7 +224,7 @@ describe("array-pop-errors", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -277,7 +277,7 @@ describe("array-pop-errors", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; diff --git a/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts b/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts index 84f8290801..1f38d460ac 100644 --- a/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-pop.int.test.ts @@ -96,7 +96,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -108,7 +108,7 @@ describe("array-pop", () => { const update = ` mutation { - ${typeMovie.operations.update} (update: { tags_POP: 1 }) { + ${typeMovie.operations.update} (update: { tags: { pop: 1} }) { ${typeMovie.plural} { title tags @@ -196,7 +196,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -208,7 +208,7 @@ describe("array-pop", () => { const update = ` mutation { - ${typeMovie.operations.update} (update: { tags_POP: 1 }) { + ${typeMovie.operations.update} (update: { tags: {pop: 1 } }) { ${typeMovie.plural} { title tags @@ -296,7 +296,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -308,7 +308,7 @@ describe("array-pop", () => { const update = ` mutation { - ${typeMovie.operations.update} (update: { tags_POP: 2 }) { + ${typeMovie.operations.update} (update: { tags: {pop: 2 } }) { ${typeMovie.plural} { title tags @@ -376,7 +376,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [Point] + tags: [Point!] } `; @@ -421,7 +421,7 @@ describe("array-pop", () => { const update = ` mutation UpdateMovie($elementsToPop: Int!) { - ${typeMovie.operations.update} (update: { tags_POP: $elementsToPop }) { + ${typeMovie.operations.update} (update: { tags: { pop: $elementsToPop } }) { ${typeMovie.plural} { title tags { @@ -487,7 +487,7 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [CartesianPoint] + tags: [CartesianPoint!] } `; @@ -530,7 +530,7 @@ describe("array-pop", () => { const update = ` mutation UpdateMovie($elementsToPop: Int!) { - ${typeMovie.operations.update} (update: { tags_POP: $elementsToPop }) { + ${typeMovie.operations.update} (update: { tags: {pop: $elementsToPop } }) { ${typeMovie.plural} { title tags { @@ -563,8 +563,8 @@ describe("array-pop", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -576,7 +576,7 @@ describe("array-pop", () => { const update = ` mutation { - ${typeMovie.operations.update} (update: { tags_POP: 1, moreTags_POP: 2 }) { + ${typeMovie.operations.update} (update: { tags: {pop: 1}, moreTags: {pop: 2} }) { ${typeMovie.plural} { title tags @@ -611,7 +611,7 @@ describe("array-pop", () => { const actor = testHelper.createUniqueType("Actor"); const typeDefs = ` type ${movie.name} @node { - viewers: [Int]! + viewers: [Int!]! workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) } type ${actor.name} @node { @@ -635,7 +635,7 @@ describe("array-pop", () => { { update: { node: { - viewers_POP: $numberToPop + viewers: {pop: $numberToPop} } } } @@ -690,7 +690,7 @@ describe("array-pop", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -707,7 +707,7 @@ describe("array-pop", () => { { update: { edge: { - pay_POP: $numberToPop + pay: {pop: $numberToPop} } } } @@ -773,7 +773,7 @@ describe("array-pop", () => { } type ActedIn @relationshipProperties { - locations: [Point] + locations: [Point!] } `; @@ -790,7 +790,7 @@ describe("array-pop", () => { { update: { edge: { - locations_POP: $numberToPop + locations: {pop: $numberToPop} } } } diff --git a/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts b/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts index f0fe245177..030fab8834 100644 --- a/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-push-errors.int.test.ts @@ -38,7 +38,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -81,7 +81,7 @@ describe("array-push", () => { const typeDefs = ` type ${typeMovie} @node { title: String - tags: [String] @authentication(operations: [UPDATE]) + tags: [String!] @authentication(operations: [UPDATE]) } `; @@ -132,7 +132,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -176,7 +176,7 @@ describe("array-push", () => { const typeDefs = /* GraphQL */ ` type ${typeMovie} @node { title: String - tags: [String] + tags: [String!] } `; @@ -229,7 +229,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -240,7 +240,7 @@ describe("array-push", () => { }); const query = /* GraphQL */ ` - mutation Mutation($id: ID, $payIncrement: [Float]) { + mutation Mutation($id: ID, $payIncrement: [Float!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { diff --git a/packages/graphql/tests/integration/array-methods/array-push.int.test.ts b/packages/graphql/tests/integration/array-methods/array-push.int.test.ts index a4eecfdb5c..3763e6e966 100644 --- a/packages/graphql/tests/integration/array-methods/array-push.int.test.ts +++ b/packages/graphql/tests/integration/array-methods/array-push.int.test.ts @@ -223,7 +223,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -297,7 +297,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -373,7 +373,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [${inputType}] + tags: [${inputType}!] } `; @@ -425,8 +425,8 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [String] - moreTags: [String] + tags: [String!] + moreTags: [String!] } `; @@ -473,7 +473,7 @@ describe("array-push", () => { const actor = testHelper.createUniqueType("Actor"); const typeDefs = ` type ${movie.name} @node { - viewers: [Int]! + viewers: [Int!]! workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) } type ${actor.name} @node { @@ -490,7 +490,7 @@ describe("array-push", () => { }); const update = ` - mutation($id: ID, $value: [Int]) { + mutation($id: ID, $value: [Int!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { worksInMovies: [ @@ -553,7 +553,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -564,7 +564,7 @@ describe("array-push", () => { }); const query = ` - mutation Mutation($id: ID, $payIncrement: [Float]) { + mutation Mutation($id: ID, $payIncrement: [Float!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { @@ -636,7 +636,7 @@ describe("array-push", () => { } type ActedIn @relationshipProperties { - locations: [Point] + locations: [Point!] } `; @@ -647,7 +647,7 @@ describe("array-push", () => { }); const query = ` - mutation Mutation($id: ID, $location: [PointInput]) { + mutation Mutation($id: ID, $location: [PointInput!]) { ${actor.operations.update}(where: { id_EQ: $id }, update: { actedIn: [ { @@ -715,7 +715,7 @@ describe("array-push", () => { const typeDefs = gql` type ${typeMovie} @node { title: String - tags: [LocalTime] + tags: [LocalTime!] } `; diff --git a/packages/graphql/tests/integration/config-options/query-options.int.test.ts b/packages/graphql/tests/integration/config-options/query-options.int.test.ts index 3db6adb016..7d46540242 100644 --- a/packages/graphql/tests/integration/config-options/query-options.int.test.ts +++ b/packages/graphql/tests/integration/config-options/query-options.int.test.ts @@ -112,7 +112,7 @@ describe("query options", () => { const result = await testHelper.executeGraphQL(query, { variableValues: { id }, - contextValue: { cypherQueryOptions: { runtime: "interpreted", addVersionPrefix: true } }, + contextValue: { cypherQueryOptions: { runtime: "interpreted", addVersionPrefix: false } }, }); expect(result.errors).toBeFalsy(); diff --git a/packages/graphql/tests/integration/config-options/startup-validation.int.test.ts b/packages/graphql/tests/integration/config-options/startup-validation.int.test.ts index eeae8a8506..9623b50f83 100644 --- a/packages/graphql/tests/integration/config-options/startup-validation.int.test.ts +++ b/packages/graphql/tests/integration/config-options/startup-validation.int.test.ts @@ -77,8 +77,8 @@ describe("Startup Validation", () => { id: ID! firstName: String! lastName: String! - friend1: User! @relationship(type: "FRIENDS_WITH", direction: IN) - friend2: User! @relationship(type: "FRIENDS_WITH", direction: IN) + friend1: [User!]! @relationship(type: "FRIENDS_WITH", direction: IN) + friend2: [User!]! @relationship(type: "FRIENDS_WITH", direction: IN) } `; @@ -88,8 +88,8 @@ describe("Startup Validation", () => { firstName: String! lastName: String! fullName: String @customResolver(requires: "firstName lastName") - friend1: User! @relationship(type: "FRIENDS_WITH", direction: IN) - friend2: User! @relationship(type: "FRIENDS_WITH", direction: IN) + friend1: [User!]! @relationship(type: "FRIENDS_WITH", direction: IN) + friend2: [User!]! @relationship(type: "FRIENDS_WITH", direction: IN) } type Point @node { diff --git a/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts b/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts deleted file mode 100644 index 2af7a5cae7..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/connect-or-create-authorization.int.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Integer } from "neo4j-driver"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("connectOrCreate", () => { - describe("Update -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let queryUpdate: string; - let queryCreate: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeGenre = testHelper.createUniqueType("Genre"); - const secret = "secret"; - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeMovie.name} @node { - title: String - genres: [${typeGenre.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${typeGenre.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - name: String @unique - } - `; - - queryUpdate = /* GraphQL */ ` - mutation { - ${typeMovie.operations.update}( - update: { - title_SET: "Forrest Gump 2" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - queryCreate = ` - mutation { - ${typeMovie.operations.create}( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Comedy" } }, - onCreate: { node: { name: "Comedy" } } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("cannot update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "RandomMovie1"})`); - const gqlResult = await testHelper.executeGraphQL(queryUpdate); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); - - test("update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "Forrest Gump"})`); - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryUpdate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Horror" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - - test("create with ConnectOrCreate auth", async () => { - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryCreate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Comedy" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - }); - - describe("authorization rules on source and target types", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let queryUpdate: string; - let queryCreate: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeGenre = testHelper.createUniqueType("Genre"); - const secret = "secret"; - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeMovie.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - title: String - genres: [${typeGenre.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${typeGenre.name} @node @authorization(validate: [{ operations: [CREATE_RELATIONSHIP, CREATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { - name: String @unique - } - `; - - queryUpdate = ` - mutation { - ${typeMovie.operations.update}( - update: { - title_SET: "Forrest Gump 2" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - queryCreate = ` - mutation { - ${typeMovie.operations.create}( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name_EQ: "Comedy" } }, - onCreate: { node: { name: "Comedy" } } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("cannot update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "RandomMovie1"})`); - const gqlResult = await testHelper.executeGraphQL(queryUpdate); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); - - test("update with ConnectOrCreate auth", async () => { - await testHelper.executeCypher(`CREATE (:${typeMovie.name} { title: "Forrest Gump"})`); - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryUpdate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Horror" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - - test("create with ConnectOrCreate auth", async () => { - const token = createBearerToken(secret, { roles: ["admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(queryCreate, token); - expect(gqlResult.errors).toBeUndefined(); - - const genreCount: any = await testHelper.executeCypher(` - MATCH (m:${typeGenre.name} { name: "Comedy" }) - RETURN COUNT(m) as count - `); - expect((genreCount.records[0].toObject().count as Integer).toNumber()).toBe(1); - }); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts b/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts deleted file mode 100644 index 07c36de3ce..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/connect-or-create-id.int.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("connect-or-create with @id", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! @unique - subtitle: String @unique - id: ID! @id @unique - actors: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type ${typeActor.name} @node { - name: String - movies: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("create -> connectOrCreate with autogenerated ID", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { title: "${title}" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.name).toBe("Tom Hanks"); - - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].title).toBe(title); - - expect(typeof resultActor.movies[0].id).toBe("string"); - }); - - test("create -> connectOrCreate errors if title is not provided onCreate", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toHaveLength(1); - expect((gqlResult?.errors as any[])[0].message).toBe( - `Field "${typeMovie.name}OnCreateInput.title" of required type "String!" was not provided.` - ); - }); - - test("create -> connectOrCreate with specified ID should throw", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "myid" } } - onCreate: { node: { id: "myid", title: "The Terminal" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toHaveLength(1); - expect((gqlResult?.errors as any[])[0].message).toBe( - `Field "id" is not defined by type "${typeMovie.name}OnCreateInput".` - ); - }); - - test("create -> connectOrCreate overrides title on create", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "${title}" } } - onCreate: { node: { title: "${title}-2" } } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].title).toBe(`${title}-2`); - }); - - // NOTE: This test case covers a known bug #1124 in which the where field update the values on connectOrCreate - /* eslint-disable-next-line jest/no-disabled-tests */ - test.skip("create -> connectOrCreate does not change value on where", async () => { - const title = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { subtitle_EQ: "${title}" } } - onCreate: { node: { title: "${title}"} } - } - } - } - ] - ) { - ${typeActor.plural} { - name, - movies { - id - title - subtitle - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toHaveLength(1); - - const resultActor = (gqlResult as any).data[typeActor.operations.create][typeActor.plural][0]; - expect(resultActor.movies).toHaveLength(1); - expect(resultActor.movies[0].subtitle).toBeNull(); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts b/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts deleted file mode 100644 index f639ad12c7..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create-union.int.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Create -> ConnectOrCreate Union", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: { - name: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts b/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts deleted file mode 100644 index b8f045b673..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/create-connect-or-create.int.test.ts +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Integer, QueryResult } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -async function runAndParseRecords>( - testHelper: TestHelper, - cypher: string, - params?: Record -): Promise { - const result = await testHelper.executeCypher(cypher, params); - return extractFirstRecord(result); -} - -function extractFirstRecord(records: QueryResult>): T { - const record = records.records[0]; - if (!record) throw new Error("Record is undefined, i.e. no columns returned from neo4j-driver in test"); - return record.toObject(); -} - -describe("Create -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - id: Int! @unique - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 22, - name: "Tom Hanks" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", id: 5 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitleAndId = await runAndParseRecords<{ title: string; id: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - ` - ); - - expect(movieTitleAndId.title).toBe("The Terminal"); - expect(movieTitleAndId.id.toNumber()).toBe(5); - - const actedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - }); - - test("ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 234, - name: "${testActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: testActorName, - }, - ]); - - const actorsWithMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (a:${typeActor.name} {name: "${testActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - ` - ); - - expect(actorsWithMovieCount.count.toInt()).toBe(1); - - const moviesWithIdCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - ` - ); - - expect(moviesWithIdCount.count.toInt()).toBe(1); - - const theTerminalMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - ` - ); - - expect(theTerminalMovieCount.count.toInt()).toBe(0); - - const actedInRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${testActorName}"}) - RETURN r.screentime as screentime - ` - ); - - expect(actedInRelation.screentime.toNumber()).toBe(105); - - const newIdMovieCount = await runAndParseRecords<{ count: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - ` - ); - expect(newIdMovieCount.count.toInt()).toBe(0); - }); - - test("ConnectOrCreate creates new node with edge data", async () => { - const actorName = "Tommy Hanks The Little"; - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - id: 239, - name: "${actorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 52 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal 2", id: 52 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: actorName, - }, - ]); - - const movieTitleAndId = await runAndParseRecords<{ title: string; id: Integer }>( - testHelper, - ` - MATCH (m:${typeMovie.name} {id: 52}) - RETURN m.title as title, m.id as id - ` - ); - - expect(movieTitleAndId.title).toBe("The Terminal 2"); - expect(movieTitleAndId.id.toNumber()).toBe(52); - - const actedInRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} {id: 52})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - ` - ); - - expect(actedInRelation.screentime.toNumber()).toBe(105); - }); - test("ConnectOrCreate creates a new node with the correct relationship direction", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeMovie.operations.create}( - input: [{ - id: 339, - title: "The Matrix", - ${typeActor.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 305 } } - onCreate: { node: { id: 305, name: "Keanu" }, edge: { screentime: 105 } } - } - } - }] - ) { - ${typeMovie.plural} { - id - title - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeMovie.operations.create][`${typeMovie.plural}`]).toEqual([ - { - id: 339, - title: "The Matrix", - }, - ]); - - const actorsRelation = await runAndParseRecords<{ screentime: Integer }>( - testHelper, - ` - MATCH (:${typeMovie.name} { id: 339 })<-[r:ACTED_IN]-(:${typeActor.name} { name: "Keanu" }) - RETURN r.screentime as screentime - ` - ); - - expect(actorsRelation.screentime.toNumber()).toBe(105); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts deleted file mode 100644 index 2d6cf7ea67..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-top-level.int.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate Top Level", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update with ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks 2" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 5 } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks 2", - }, - ]); - - const movieTitleAndId: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0].toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0].toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks 2"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - }); - - test("Update with ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - const updatedActorName = "updatedActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${testActorName}"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "${updatedActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - where: { name_EQ: "${testActorName}"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: updatedActorName, - }, - ]); - - const actorsWithMovieCount: any = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name: "${updatedActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0].toObject().count.toInt()).toBe(1); - - const moviesWithIdCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - `); - - expect(moviesWithIdCount.records[0].toObject().count.toInt()).toBe(1); - - const theTerminalMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - `); - - expect(theTerminalMovieCount.records[0].toObject().count.toInt()).toBe(0); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${updatedActorName}"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - - const newIdMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - `); - expect(newIdMovieCount.records[0].toObject().count.toInt()).toBe(0); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts deleted file mode 100644 index b0674fd578..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union-top-level.int.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate union top level", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new nodes", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${actorName}"})`); - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - where: { name_EQ: "${actorName}"}){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts deleted file mode 100644 index ca15ed4973..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create-union.int.test.ts +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate Union", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeSeries = testHelper.createUniqueType("Series"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitle = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {isan: "${movieIsan}"}) - RETURN m.title as title - `); - - expect(movieTitle.records).toHaveLength(1); - expect(movieTitle.records[0]?.toObject().title).toBe("Forrest Gump"); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesTitle = await testHelper.executeCypher(` - MATCH (m:${typeSeries.name} {isan: "${seriesIsan}"}) - RETURN m.title as title - `); - - expect(seriesTitle.records).toHaveLength(1); - expect(seriesTitle.records[0]?.toObject().title).toBe("Band of Brothers"); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); - - test("ConnectOrCreate on existing node", async () => { - const movieIsan = "xx0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "xx0000-0001-ECC5-0000-8-0000-0001-B"; - const actorName = "Tom Hanks evil twin"; - - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${actorName}"})`); - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Forrest Gump", isan:"${movieIsan}"})`); - await testHelper.executeCypher( - `CREATE (m:${typeSeries.name} { title: "Band of Brothers", isan:"${seriesIsan}"})` - ); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "${actorName}" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }, - where: { - name_EQ: "${actorName}" - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: actorName, - }, - ]); - - const actorsWithMovieCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeMovie.name} {isan:"${movieIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0]?.toObject().count.toInt()).toBe(1); - - const actorsWithSeriesCount = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name:"${actorName}"})-[]->(:${typeSeries.name} {isan:"${seriesIsan}"}) - RETURN COUNT(a) as count - `); - - expect(actorsWithSeriesCount.records[0]?.toObject().count.toInt()).toBe(1); - - const movieActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {isan: "${movieIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(movieActedInRelation.records).toHaveLength(1); - expect((movieActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(105); - - const seriesActedInRelation = await testHelper.executeCypher(` - MATCH (:${typeSeries.name} {isan: "${seriesIsan}"})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${actorName}"}) - RETURN r.screentime as screentime - `); - - expect(seriesActedInRelation.records).toHaveLength(1); - expect((seriesActedInRelation.records[0]?.toObject().screentime as Integer).toNumber()).toBe(126); - }); -}); diff --git a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts b/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts deleted file mode 100644 index c6b70a7207..0000000000 --- a/packages/graphql/tests/integration/connect-or-create/update-connect-or-create.int.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update -> ConnectOrCreate", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeEach(async () => { - typeDefs = /* GraphQL */ ` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update with ConnectOrCreate creates new node", async () => { - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "Tom Hanks"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks 2" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 5 } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][typeActor.plural]).toEqual([ - { - name: "Tom Hanks 2", - }, - ]); - - const movieTitleAndId: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0].toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0].toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks 2"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - }); - - test("Update with ConnectOrCreate on existing node", async () => { - const testActorName = "aRandomActor"; - const updatedActorName = "updatedActor"; - await testHelper.executeCypher(`CREATE (m:${typeMovie.name} { title: "Terminator2", id: 2222})`); - await testHelper.executeCypher(`CREATE (:${typeActor.name} { name: "${testActorName}"})`); - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "${updatedActorName}" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 2222 } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal", id: 22224 } } - } - } - } - where: { name_EQ: "${testActorName}"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.update][`${typeActor.plural}`]).toEqual([ - { - name: updatedActorName, - }, - ]); - - const actorsWithMovieCount: any = await testHelper.executeCypher(` - MATCH (a:${typeActor.name} {name: "${updatedActorName}"})-[]->(m:${typeMovie.name} {id: 2222}) - RETURN COUNT(a) as count - `); - - expect(actorsWithMovieCount.records[0].toObject().count.toInt()).toBe(1); - - const moviesWithIdCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222}) - RETURN COUNT(m) as count - `); - - expect(moviesWithIdCount.records[0].toObject().count.toInt()).toBe(1); - - const theTerminalMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 2222, name: "The Terminal"}) - RETURN COUNT(m) as count - `); - - expect(theTerminalMovieCount.records[0].toObject().count.toInt()).toBe(0); - - const actedInRelation: any = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 2222})<-[r:ACTED_IN]-(:${typeActor.name} {name: "${updatedActorName}"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - expect((actedInRelation.records[0].toObject().screentime as Integer).toNumber()).toBe(105); - - const newIdMovieCount: any = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 22224}) - RETURN COUNT(m) as count - `); - expect(newIdMovieCount.records[0].toObject().count.toInt()).toBe(0); - }); -}); diff --git a/packages/graphql/tests/integration/connections/alias.int.test.ts b/packages/graphql/tests/integration/connections/alias.int.test.ts index f40e7339c9..a0fef078d6 100644 --- a/packages/graphql/tests/integration/connections/alias.int.test.ts +++ b/packages/graphql/tests/integration/connections/alias.int.test.ts @@ -608,7 +608,7 @@ describe("Connections Alias", () => { } type ActedIn @relationshipProperties { - roles: [String]! + roles: [String!]! } `; @@ -663,7 +663,7 @@ describe("Connections Alias", () => { } type ActedIn @relationshipProperties { - roles: [String]! + roles: [String!]! } `; @@ -743,7 +743,7 @@ describe("Connections Alias", () => { } type Comment @node { flag: Boolean! - post: Post! @relationship(type: "HAS_COMMENT", direction: IN) + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) } `; diff --git a/packages/graphql/tests/integration/create.int.test.ts b/packages/graphql/tests/integration/create.int.test.ts index eb94a0863d..57b8007fe7 100644 --- a/packages/graphql/tests/integration/create.int.test.ts +++ b/packages/graphql/tests/integration/create.int.test.ts @@ -219,7 +219,7 @@ describe("create", () => { id: ID! description: String! url: String! - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; diff --git a/packages/graphql/tests/integration/cypher-params.int.test.ts b/packages/graphql/tests/integration/cypher-params.int.test.ts index 421c74cf89..639f86ffdc 100644 --- a/packages/graphql/tests/integration/cypher-params.int.test.ts +++ b/packages/graphql/tests/integration/cypher-params.int.test.ts @@ -52,7 +52,7 @@ describe("cypherParams", () => { charset: "alphabetic", }); - const source = ` + const source = /* GraphQL */ ` { id } @@ -69,7 +69,7 @@ describe("cypherParams", () => { test("should inject cypherParams on field level nested query", async () => { const typeDefs = /* GraphQL */ ` - type CypherParams { + type CypherParams @node { id: ID } @@ -90,7 +90,7 @@ describe("cypherParams", () => { charset: "alphabetic", }); - const source = ` + const source = /* GraphQL */ ` query($id: ID) { ${Movie.plural}(where: {id_EQ: $id}) { id @@ -144,7 +144,7 @@ describe("cypherParams", () => { charset: "alphabetic", }); - const source = ` + const source = /* GraphQL */ ` mutation { id } diff --git a/packages/graphql/tests/integration/delete-interface.int.test.ts b/packages/graphql/tests/integration/delete-interface.int.test.ts index 60674e1be9..1cbc298fc9 100644 --- a/packages/graphql/tests/integration/delete-interface.int.test.ts +++ b/packages/graphql/tests/integration/delete-interface.int.test.ts @@ -49,7 +49,7 @@ describe("delete interface relationships", () => { const typeDefs = gql` type ${episodeType.name} @node { runtime: Int! - series: ${seriesType.name} ! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${seriesType.name}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -139,9 +139,9 @@ describe("delete interface relationships", () => { }); test("should delete one nested concrete entity", async () => { - const query = ` + const query = /* GraphQL */ ` mutation DeleteActorAndMovie($name: String, $title: String) { - ${actorType.operations.delete}(where: { name_EQ: $name }, delete: { actedIn: { where: { node: { typename_IN: [${movieType}], title_EQ: $title } } } } ) { + ${actorType.operations.delete}(where: { name: { eq: $name } }, delete: { actedIn: { where: { node: { typename: [${movieType}], title: { eq: $title} } } } } ) { nodesDeleted relationshipsDeleted } @@ -163,13 +163,13 @@ describe("delete interface relationships", () => { }); test("should delete one nested concrete entity using interface relationship fields", async () => { - const query = ` + const query = /* GraphQL */ ` mutation DeleteActorAndMovie($name1: String, $movieScreenTime1: Int) { ${actorType.operations.delete}( - where: { name_EQ: $name1 } + where: { name: { eq: $name1 } } delete: { actedIn: { - where: { edge: { screenTime_EQ: $movieScreenTime1 } } + where: { edge: { screenTime: { eq: $movieScreenTime1 } } } } } ) { @@ -194,13 +194,13 @@ describe("delete interface relationships", () => { }); test("should delete two nested concrete entity using interface relationship fields", async () => { - const query = ` + const query = /* GraphQL */ ` mutation DeleteActorAndMovie($name1: String, $movieScreenTime1: Int, $movieScreenTime2: Int) { ${actorType.operations.delete}( - where: { name_EQ: $name1 } + where: { name: { eq: $name1 } } delete: { actedIn: { - where: { edge: { OR: [ {screenTime_EQ: $movieScreenTime1 }, { screenTime_EQ: $movieScreenTime2 } ]} } + where: { edge: { OR: [ {screenTime: { eq: $movieScreenTime1 } }, { screenTime: { eq: $movieScreenTime2 } } ]} } } } ) { @@ -229,16 +229,16 @@ describe("delete interface relationships", () => { }); test("should be possible to double nested delete", async () => { - const query = ` + const query = /* GraphQL */ ` mutation DeleteActorAndMovie($name1: String, $movieTitle2: String, $name2: String) { ${actorType.operations.delete}( - where: { name_EQ: $name1 } + where: { name: { eq: $name1 } } delete: { actedIn: { - where: { node: { title_EQ: $movieTitle2 } } + where: { node: { title: { eq: $movieTitle2 }} } delete: { actors: { - where: { node: { name_EQ: $name2 } } + where: { node: { name: { eq: $name2} } } } } } diff --git a/packages/graphql/tests/integration/delete-union.int.test.ts b/packages/graphql/tests/integration/delete-union.int.test.ts index d90e4696b9..a059e06745 100644 --- a/packages/graphql/tests/integration/delete-union.int.test.ts +++ b/packages/graphql/tests/integration/delete-union.int.test.ts @@ -49,7 +49,7 @@ describe("delete union relationships", () => { const typeDefs = gql` type ${episodeType.name} @node { runtime: Int! - series: ${seriesType.name} ! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${seriesType.name}!]! @relationship(type: "HAS_EPISODE", direction: IN) } union Production = ${movieType.name} | ${seriesType.name} diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/AND-OR-operations.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/AND-OR-operations.int.test.ts new file mode 100644 index 0000000000..ea8f6aeaf5 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/AND-OR-operations.int.test.ts @@ -0,0 +1,456 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Nested within AND/OR", () => { + const testHelper = new TestHelper(); + + let userType: UniqueType; + let postType: UniqueType; + + const testString1 = "This is a test string"; + const testString2 = "anotherTestString"; + const testString3 = "SomeUserString!"; + const testString4 = "Foo"; + const testString5 = "Baa"; + const content1 = "SomeContent!"; + const content2 = "testContent"; + const content3 = "testContent3"; + const content4 = "Baz"; + const content5 = "Some more content"; + + beforeEach(async () => { + userType = testHelper.createUniqueType("User"); + postType = testHelper.createUniqueType("Post"); + + const typeDefs = ` + type ${userType.name} @node { + testString: String! + } + + type ${postType.name} @node { + content: String! + likes: [${userType.name}!]! @relationship(type: "LIKES", direction: IN) + } + `; + + await testHelper.executeCypher(` + CREATE (post1:${postType.name} { content: "${content1}" })<-[:LIKES]-(user1:${userType.name} { testString: "${testString1}" }) + CREATE (post2:${postType.name} { content: "${content2}" })<-[:LIKES]-(user2:${userType.name} { testString: "${testString2}" }) + CREATE (post3:${postType.name} { content: "${content3}" })<-[:LIKES]-(user3:${userType.name} { testString: "${testString3}" }) + CREATE (post4:${postType.name} { content: "${content4}" })<-[:LIKES]-(user4:${userType.name} { testString: "${testString4}" }) + CREATE (post5:${postType.name} { content: "${content5}" })<-[:LIKES]-(user5:${userType.name} { testString: "${testString5}" }) + MERGE (post1)<-[:LIKES]-(user2) + MERGE (post1)<-[:LIKES]-(user3) + MERGE (post2)<-[:LIKES]-(user4) + MERGE (post2)<-[:LIKES]-(user5) + MERGE (post3)<-[:LIKES]-(user1) + `); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("Implicit AND", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + count_EQ: 3 + node: { + testString_SHORTEST_LENGTH_EQUAL: 3 + } + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: [ + { + content: content2, + }, + ], + }); + }); + + test("Top-level OR", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + OR: [ + { count_EQ: 3 } + { + node: { + testString_SHORTEST_LENGTH_EQUAL: 3 + } + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content1, + }, + { + content: content2, + }, + { + content: content4, + }, + { + content: content5, + }, + ]), + }); + }); + + test("Top-level AND", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + AND: [ + { count_EQ: 3 } + { + node: { + testString_SHORTEST_LENGTH_EQUAL: 3 + } + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: [ + { + content: content2, + }, + ], + }); + }); + + test("AND within an AND", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + AND: [ + { count_LTE: 2 } + { + AND: [ + { + node: { + testString_SHORTEST_LENGTH_LT: 4 + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 2 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content5, + }, + { + content: content4, + }, + ]), + }); + }); + + test("AND within an AND with NOT", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + AND: [ + { NOT: { count_GT: 2 } } + { + AND: [ + { + node: { + NOT: { testString_SHORTEST_LENGTH_GT: 4 } + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 2 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content5, + }, + { + content: content4, + }, + ]), + }); + }); + + test("OR within an OR", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + OR: [ + { count_LTE: 2 } + { + OR: [ + { + node: { + testString_SHORTEST_LENGTH_LT: 4 + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 20 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content2, + }, + { + content: content3, + }, + { + content: content4, + }, + { + content: content5, + }, + ]), + }); + }); + + test("OR within an AND", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + AND: [ + { count_LTE: 2 } + { + OR: [ + { + node: { + testString_SHORTEST_LENGTH_LT: 4 + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 20 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content4, + }, + { + content: content5, + }, + ]), + }); + }); + + test("OR within an AND with NOT", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + AND: [ + { NOT: { count_GT: 2 } } + { + OR: [ + { + node: { + NOT: { testString_SHORTEST_LENGTH_GT: 4 } + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 20 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content4, + }, + { + content: content5, + }, + ]), + }); + }); + + test("AND within an OR", async () => { + const query = ` + query { + ${postType.plural}(where: { + likesAggregate: { + OR: [ + { count_GTE: 2 } + { + AND: [ + { + node: { + testString_SHORTEST_LENGTH_LT: 4 + } + } + { + node: { + testString_SHORTEST_LENGTH_GT: 2 + } + } + ] + } + ] + } + }) { + content + } + } + `; + + const result = await testHelper.executeGraphQL(query); + + expect(result.errors).toBeFalsy(); + expect(result.data).toEqual({ + [postType.plural]: expect.toIncludeSameMembers([ + { + content: content1, + }, + { + content: content2, + }, + { + content: content3, + }, + { + content: content4, + }, + { + content: content5, + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/count.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/count.int.test.ts new file mode 100644 index 0000000000..2999e420d1 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/count.int.test.ts @@ -0,0 +1,497 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("aggregations-where-count", () => { + const testHelper = new TestHelper(); + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${User} @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the count of likes equal one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = /* GraphQL */ ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_EQ: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString }], + }, + ]); + }); + + test("should return posts where the count of likes LT one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LT: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [], + }, + ]); + }); + + test("should return posts where the count of likes LTE one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LTE: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString }], + }, + { + testString, + likes: [], + }, + ]); + }); + + test("should return posts where the count of likes GT one, regardless of number of likes over 1", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GT: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: expect.toIncludeSameMembers([{ testString }, { testString }]), + }, + ]); + }); + + test("should return posts where the count of likes GT one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GTE: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString }], + }, + ]); + }); +}); + +describe("aggregations-where-count interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! + } + + type ${Person} implements Human @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the count of likes equal one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_EQ: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString }], + }, + ]); + }); + + test("should return posts where the count of likes LT one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LT: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [], + }, + ]); + }); + + test("should return posts where the count of likes LTE one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_LTE: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString }], + }, + { + testString, + likes: [], + }, + ]); + }); + + test("should return posts where the count of likes GT one, regardless of number of likes over 1", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GT: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: expect.toIncludeSameMembers([{ testString }, { testString }]), + }, + ]); + }); + + test("should return posts where the count of likes GT one", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { count_GTE: 1 } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString }], + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/edge/int.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/edge/int.int.test.ts new file mode 100644 index 0000000000..3f44ad5716 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/edge/int.int.test.ts @@ -0,0 +1,294 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-edge-int", () => { + const testHelper = new TestHelper(); + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + + const typeDefs = ` + type ${User} @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + } + + type Likes @relationshipProperties { + someInt: Int + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("AVERAGE", () => { + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + test("should return posts where the average of a edge like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of a edge like Int's is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_GT: ${avgGT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of a edge like Int's is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_GTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of a edge like Int's is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_LT: ${avgLT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of a edge like Int's is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_AVERAGE_LTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); + + describe("sum", () => { + test("should return posts where the sum of a edge like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + const sum = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt1} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt2} }]-(:${User} {testString: "${testString}"}) + CREATE (p)<-[:LIKES { someInt: ${someInt3} }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { someInt_SUM_EQUAL: ${sum} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/edge/string.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/edge/string.int.test.ts new file mode 100644 index 0000000000..acfcab9430 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/edge/string.int.test.ts @@ -0,0 +1,940 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-edge-string", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${User} @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + } + + type Likes @relationshipProperties { + testString: String + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the SHORTEST edge like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${shortestTestString}" }]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${longestTestString}" }]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString: shortestTestString }], + }, + ]); + }); + + test("should return posts where the LONGEST edge like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${shortestTestString}" }]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${longestTestString}" }]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString: longestTestString }], + }, + ]); + }); + + describe("AVERAGE", () => { + test("should return posts where the AVERAGE of edge like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-edge-string interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + const typeDefs = /* GraphQL */ ` + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! + } + + type ${Person} implements Human @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + someStringAlias: String @alias(property: "_someStringAlias") + } + + type Likes @relationshipProperties { + testString: String + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the SHORTEST edge like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${shortestTestString}" }]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${longestTestString}" }]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString: shortestTestString }], + }, + ]); + }); + + test("should return posts where the LONGEST edge like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${shortestTestString}" }]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES { testString: "${longestTestString}" }]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toEqual([ + { + testString, + likes: [{ testString: longestTestString }], + }, + ]); + }); + + describe("AVERAGE", () => { + test("should return posts where the %s of edge like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of edge like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString1}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString2}" }]-(:${User} {testString: "${testString}"}) + CREATE(p)<-[:LIKES { testString: "${testString3}" }]-(:${User} {testString: "${testString}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { edge: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/node/int-connections.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/node/int-connections.int.test.ts new file mode 100644 index 0000000000..d321e1cdc3 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/node/int-connections.int.test.ts @@ -0,0 +1,634 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-node-int - connections", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + const typeDefs = ` + type ${User} @node { + testString: String! + someInt: Int! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("AVERAGE", () => { + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + test("should return posts where the average of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); + + describe("sum", () => { + test("should return posts where the sum of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + const sum = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-node-int - connections - interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + + const typeDefs = ` + interface Human { + testString: String! + someInt: Int! + } + + type ${Person} implements Human @node { + testString: String! + someInt: Int! + } + type ${User} implements Human @node { + testString: String! + someInt: Int! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("AVERAGE", () => { + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + test("should return posts where the average of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); + + describe("sum", () => { + test("should return posts where the sum of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + const sum = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + edges { + node { + testString + likes { + testString + someInt + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/node/int.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/node/int.int.test.ts new file mode 100644 index 0000000000..f94e3869b8 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/node/int.int.test.ts @@ -0,0 +1,594 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-node-int", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + + const typeDefs = ` + interface Human { + testString: String! + someInt: Int! + } + + type ${Person} implements Human @node { + testString: String! + someInt: Int! + } + type ${User} implements Human @node { + testString: String! + someInt: Int! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("AVERAGE", () => { + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + test("should return posts where the average of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); + + describe("sum", () => { + test("should return posts where the sum of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + const sum = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-node-int interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + const typeDefs = ` + interface Human { + testString: String! + someInt: Int! + } + + type ${Person} implements Human @node { + testString: String! + someInt: Int! + } + type ${User} implements Human @node { + testString: String! + someInt: Int! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("AVERAGE", () => { + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + test("should return posts where the average of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_EQUAL: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GT: ${avgGT} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_GTE: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LT: ${avgLT} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Int's is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const avg = (someInt1 + someInt2 + someInt3) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_AVERAGE_LTE: ${avg} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); + + describe("sum", () => { + test("should return posts where the sum of like Int's is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const someInt1 = 1; + const someInt2 = 2; + const someInt3 = 3; + + const sum = someInt1 + someInt2 + someInt3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt1}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt2}}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString}", someInt: ${someInt3}}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { someInt_SUM_EQUAL: ${sum} } } }) { + testString + likes { + testString + someInt + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/node/string-connections.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/node/string-connections.int.test.ts new file mode 100644 index 0000000000..5e734b9823 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/node/string-connections.int.test.ts @@ -0,0 +1,1122 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-node-string - connections", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${User} @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the %s like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.operations.connection]).toEqual({ + edges: [ + { + node: { + testString, + likes: [{ testString: shortestTestString }], + }, + }, + ], + }); + }); + + test("should return posts where the LONGEST like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.operations.connection]).toEqual({ + edges: [ + { + node: { + testString, + likes: [{ testString: longestTestString }], + }, + }, + ], + }); + }); + + describe("AVERAGE", () => { + test("should return posts where the %s of like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-node-string - connections - interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! + } + + type ${Person} implements Human @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the %s like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.operations.connection]).toEqual({ + edges: [ + { + node: { + testString, + likes: [{ testString: shortestTestString }], + }, + }, + ], + }); + }); + + test("should return posts where the LONGEST like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.operations.connection]).toEqual({ + edges: [ + { + node: { + testString, + likes: [{ testString: longestTestString }], + }, + }, + ], + }); + }); + + describe("AVERAGE", () => { + test("should return posts where the AVERAGE of like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = ((gqlResult.data as any)[Post.operations.connection] as any[])["edges"]; + expect(post.node.testString).toEqual(testString); + expect(post.node.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-node-string - connections - relationships of interface types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(() => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the SHORTEST like String is EQUAL to", async () => { + const typeDefs = /* GraphQL */ ` + interface Thing { + testString: String! + likes: [Human!]! @declareRelationship + } + + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! @alias(property: "user_testString") + } + + type ${Person} implements Human @node { + testString: String! @alias(property: "person_testString") + } + + type ${Post} implements Thing @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.operations.connection}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + edges { + node { + testString + likes { + testString + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.operations.connection].edges).toIncludeSameMembers([ + { + node: { + testString, + likes: [{ testString: shortestTestString }], + }, + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/aggregations/where/node/string.int.test.ts b/packages/graphql/tests/integration/deprecations/aggregations/where/node/string.int.test.ts new file mode 100644 index 0000000000..76da67c4b4 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/aggregations/where/node/string.int.test.ts @@ -0,0 +1,1044 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../../utils/graphql-types"; +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("aggregations-where-node-string", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${User} @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [${User}!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the SHORTEST like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString: shortestTestString }], + }, + ]); + }); + + test("should return posts where the LONGEST like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString: longestTestString }], + }, + ]); + }); + + test("should return posts where the AVERAGE of like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); +}); + +describe("aggregations-where-node-string interface relationships of concrete types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(async () => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! + } + + type ${Person} implements Human @node { + testString: String! + } + + type ${Post} @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("SHORTEST", () => { + test("should return posts where the %s like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString: shortestTestString }], + }, + ]); + }); + }); + + describe("LONGEST", () => { + test("should return posts where the %s like String is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_LONGEST_LENGTH_EQUAL: ${longestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString: longestTestString }], + }, + ]); + }); + }); + + describe("AVERAGE", () => { + test("should return posts where the AVERAGE of like Strings is EQUAL to", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_EQUAL: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgGT = avg - 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GT: ${avgGT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is GTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_GTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LT than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + const avgLT = avg + 1; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LT: ${avgLT} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + + test("should return posts where the average of like Strings is LTE than", async () => { + const testString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString1 = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const testString3 = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + const avg = (10 + 11 + 12) / 3; + + await testHelper.executeCypher( + ` + CREATE (p:${Post} {testString: "${testString}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString1}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString2}"}) + CREATE (p)<-[:LIKES]-(:${User} {testString: "${testString3}"}) + CREATE (:${Post} {testString: "${testString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_AVERAGE_LENGTH_LTE: ${avg} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural] as any[]; + expect(post.testString).toEqual(testString); + expect(post.likes).toHaveLength(3); + }); + }); +}); + +describe("aggregations-where-node-string relationships of interface types", () => { + let testHelper: TestHelper; + let User: UniqueType; + let Post: UniqueType; + let Person: UniqueType; + + beforeEach(() => { + testHelper = new TestHelper(); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + Person = testHelper.createUniqueType("Person"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return posts where the SHORTEST like String is EQUAL to", async () => { + const typeDefs = /* GraphQL */ ` + interface Thing { + testString: String! + likes: [Human!]! @declareRelationship + } + + interface Human { + testString: String! + } + + type ${User} implements Human @node { + testString: String! @alias(property: "user_testString") + } + + type ${Person} implements Human @node { + testString: String! @alias(property: "person_testString") + } + + type ${Post} implements Thing @node { + testString: String! + likes: [Human!]! @relationship(type: "LIKES", direction: IN) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const testString = generate({ + charset: "alphabetic", + readable: true, + }); + + const shortestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 10, + }); + + const testString2 = generate({ + charset: "alphabetic", + readable: true, + length: 11, + }); + + const longestTestString = generate({ + charset: "alphabetic", + readable: true, + length: 12, + }); + + await testHelper.executeCypher( + ` + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${shortestTestString}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${testString2}"}) + CREATE (:${Post} {testString: "${testString}"})<-[:LIKES]-(:${User} {user_testString: "${longestTestString}"}) + ` + ); + + const query = ` + { + ${Post.plural}(where: { testString_EQ: "${testString}", likesAggregate: { node: { testString_SHORTEST_LENGTH_EQUAL: ${shortestTestString.length} } } }) { + testString + likes { + testString + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[Post.plural]).toIncludeSameMembers([ + { + testString, + likes: [{ testString: shortestTestString }], + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/array-pop-and-push.int.test.ts b/packages/graphql/tests/integration/deprecations/array-pop-and-push.int.test.ts new file mode 100644 index 0000000000..7e3e1468fe --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/array-pop-and-push.int.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { gql } from "graphql-tag"; +import { generate } from "randomstring"; +import { TestHelper } from "../../utils/tests-helper"; + +describe("array-pop-and-push", () => { + const testHelper = new TestHelper(); + + beforeEach(() => {}); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should push to and pop from two different arrays in the same update", async () => { + const typeMovie = testHelper.createUniqueType("Movie"); + + const typeDefs = gql` + type ${typeMovie} @node { + title: String + tags: [String!] + moreTags: [String!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const movieTitle = generate({ + charset: "alphabetic", + }); + + const update = ` + mutation { + ${typeMovie.operations.update} (update: { tags_PUSH: "new tag", moreTags_POP: 2 }) { + ${typeMovie.plural} { + title + tags + moreTags + } + } + } + `; + + const cypher = ` + CREATE (m:${typeMovie} {title:$movieTitle, tags: ["abc"], moreTags: ["this", "that", "them"] }) + `; + + await testHelper.executeCypher(cypher, { movieTitle }); + + const gqlResult = await testHelper.executeGraphQL(update); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[typeMovie.operations.update][typeMovie.plural]).toEqual([ + { title: movieTitle, tags: ["abc", "new tag"], moreTags: ["this"] }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/conflicting-pagination-arguments.int.test.ts b/packages/graphql/tests/integration/deprecations/conflicting-pagination-arguments.int.test.ts deleted file mode 100644 index 2cb9bc6611..0000000000 --- a/packages/graphql/tests/integration/deprecations/conflicting-pagination-arguments.int.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GraphQLError } from "graphql"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Conflicting Pagination arguments", () => { - const testHelper = new TestHelper(); - let Movie: UniqueType; - let Actor: UniqueType; - const id1 = "A"; - const id2 = "B"; - const id3 = "C"; - - beforeEach(async () => { - Movie = testHelper.createUniqueType("Movie"); - Actor = testHelper.createUniqueType("Actor"); - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type ${Movie} @node { - id: ID! - title: String! - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher( - `CREATE (:${Movie} { id: "${id1}" }), (:${Movie} { id: "${id2}" }), (:${Movie} { id: "${id3}" })` - ); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should raise an error when deprecated pagination options are used together with pagination arguments", async () => { - const query = /* GraphQL */ ` - query { - ${Movie.plural}(sort: [{ id: DESC }], options: { sort: { id: DESC }}){ - id - } - } - `; - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toEqual([ - new GraphQLError( - "Ambiguous pagination found. The options argument is deprecated. Please use the sort, limit, and offset arguments directly on the field." - ), - ]); - }); - test("should raise an error when deprecated pagination options are used together with pagination arguments on related fields", async () => { - const query = /* GraphQL */ ` - query { - ${Movie.plural}{ - actors(sort: [{ name: DESC }], options: { sort: { name: DESC }}){ - name - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toEqual([ - new GraphQLError( - "Ambiguous pagination found. The options argument is deprecated. Please use the sort, limit, and offset arguments directly on the field." - ), - ]); - }); -}); diff --git a/packages/graphql/tests/integration/custom-scalar-filtering.int.test.ts b/packages/graphql/tests/integration/deprecations/custom-scalar-filtering.int.test.ts similarity index 99% rename from packages/graphql/tests/integration/custom-scalar-filtering.int.test.ts rename to packages/graphql/tests/integration/deprecations/custom-scalar-filtering.int.test.ts index b920e3a855..6264c97d30 100644 --- a/packages/graphql/tests/integration/custom-scalar-filtering.int.test.ts +++ b/packages/graphql/tests/integration/deprecations/custom-scalar-filtering.int.test.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import { TestHelper } from "../utils/tests-helper"; +import { TestHelper } from "../../utils/tests-helper"; describe("Custom Scalar Filtering", () => { const testHelper = new TestHelper(); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/advanced-filtering-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/advanced-filtering-deprecated.int.test.ts new file mode 100644 index 0000000000..5304ae5505 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/advanced-filtering-deprecated.int.test.ts @@ -0,0 +1,1878 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("Advanced Filtering - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + describe.each(["ID", "String"] as const)("%s Filtering", (type) => { + test("should find Movies IN strings", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + const randomValue1 = generate({ + readable: true, + charset: "alphabetic", + }); + + const randomValue2 = generate({ + readable: true, + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { property_IN: ["${value}", "${randomValue1}", "${randomValue2}"] }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + + test("should find Movies REGEX", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + filters: { + [type]: { + MATCHES: true, + }, + }, + }, + }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value: `${value}${value}` } + ); + + const query = ` + { + ${randomType.plural}(where: { property_MATCHES: "(?i)${value}.*" }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toBe(`${value}${value}`); + }); + + test("should find Movies NOT string", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + const randomValue1 = generate({ + readable: true, + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $randomValue1}) + `, + { value, randomValue1 } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property_EQ: "${randomValue1}" } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + + test("should find Movies CONTAINS string", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + const superValue = `${value}${value}`; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, + { superValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_CONTAINS: "${value}" }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(3); + + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(superValue); + }); + + test("should find Movies STARTS_WITH string", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + const superValue = `${value}${value}`; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, + { superValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_STARTS_WITH: "${value}" }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(3); + + ((gqlResult.data as any)[randomType.plural] as any[]).forEach((x) => { + expect(x.property).toEqual(superValue); + }); + }); + + test("should find Movies ENDS_WITH string", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = generate({ + readable: true, + charset: "alphabetic", + }); + + const notValue = generate({ + readable: true, + charset: "alphabetic", + }); + + const superValue = `${value}${value}`; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $notValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, + { value, notValue, superValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_ENDS_WITH: "${value}" }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + }); + }); + + describe("String Filtering", () => { + test("should find Movies implicit EQ string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const animatrix = "The Animatrix"; + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, + { animatrix, matrix, matrixReloaded, matrixRevolutions } + ); + + const query = ` + { + ${movieType.plural}(where: { title_EQ: "${matrix}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[movieType.plural]).toEqual(expect.arrayContaining([{ title: matrix }])); + }); + + test("should find Movies EQ string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const animatrix = "The Animatrix"; + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, + { animatrix, matrix, matrixReloaded, matrixRevolutions } + ); + + const query = ` + { + ${movieType.plural}(where: { title_EQ: "${matrix}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[movieType.plural]).toEqual(expect.arrayContaining([{ title: matrix }])); + }); + + test("should find Movies GT string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + features: { + filters: { + String: { + LT: true, + GT: true, + LTE: true, + GTE: true, + }, + }, + }, + typeDefs, + }); + + const animatrix = "The Animatrix"; + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, + { animatrix, matrix, matrixReloaded, matrixRevolutions } + ); + + const query = ` + { + ${movieType.plural}(where: { title_GT: "${matrix}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(2); + expect((gqlResult.data as any)[movieType.plural]).toEqual( + expect.arrayContaining([{ title: matrixReloaded }, { title: matrixRevolutions }]) + ); + }); + + test("should find Movies LT string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + features: { + filters: { + String: { + LT: true, + GT: true, + LTE: true, + GTE: true, + }, + }, + }, + typeDefs, + }); + + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + const matrixResurrections = "The Matrix Resurrections"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + CREATE (:${movieType.name} {title: $matrixResurrections}) + `, + { matrix, matrixReloaded, matrixRevolutions, matrixResurrections } + ); + + const query = ` + { + ${movieType.plural}(where: { title_LT: "${matrixRevolutions}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(3); + expect((gqlResult.data as any)[movieType.plural]).toEqual( + expect.arrayContaining([{ title: matrix }, { title: matrixReloaded }, { title: matrixResurrections }]) + ); + }); + + test("should find Movies GTE string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + features: { + filters: { + String: { + LT: true, + GT: true, + LTE: true, + GTE: true, + }, + }, + }, + typeDefs, + }); + + const animatrix = "The Animatrix"; + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, + { animatrix, matrix, matrixReloaded, matrixRevolutions } + ); + + const query = ` + { + ${movieType.plural}(where: { title_GTE: "${matrix}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(3); + expect((gqlResult.data as any)[movieType.plural]).toEqual( + expect.arrayContaining([{ title: matrix }, { title: matrixReloaded }, { title: matrixRevolutions }]) + ); + }); + + test("should find Movies LTE string", async () => { + const movieType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${movieType.name} @node { + title: String + } + `; + + await testHelper.initNeo4jGraphQL({ + features: { + filters: { + String: { + LT: true, + GT: true, + LTE: true, + GTE: true, + }, + }, + }, + typeDefs, + }); + + const matrix = "The Matrix"; + const matrixReloaded = "The Matrix Reloaded"; + const matrixRevolutions = "The Matrix Revolutions"; + const matrixResurrections = "The Matrix Resurrections"; + + await testHelper.executeCypher( + ` + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + CREATE (:${movieType.name} {title: $matrixResurrections}) + + `, + { matrix, matrixReloaded, matrixRevolutions, matrixResurrections } + ); + + const query = ` + { + ${movieType.plural}(where: { title_LTE: "${matrixRevolutions}" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + if (gqlResult.errors) { + console.log(JSON.stringify(gqlResult.errors, null, 2)); + } + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[movieType.plural]).toHaveLength(4); + expect((gqlResult.data as any)[movieType.plural]).toEqual( + expect.arrayContaining([ + { title: matrix }, + { title: matrixReloaded }, + { title: matrixRevolutions }, + { title: matrixResurrections }, + ]) + ); + }); + }); + + describe.each(["Int", "Float"] as const)("%s Filtering", (type) => { + test("should find Movies NOT number", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let property: number; + + if (type === "Int") { + property = Math.floor(Math.random() * 9999); + } else { + property = Math.floor(Math.random() * 9999) + 0.5; + } + + let notProperty: number; + + if (type === "Int") { + notProperty = Math.floor(Math.random() * 9999); + } else { + notProperty = Math.floor(Math.random() * 9999) + 0.5; + } + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $property}) + CREATE (:${randomType.name} {property: $notProperty}) + `, + { property, notProperty } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property_EQ: ${notProperty} } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(property); + }); + + test("should find Movies IN numbers", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let value: number; + + if (type === "Int") { + value = Math.floor(Math.random() * 9999); + } else { + value = Math.floor(Math.random() * 9999) + 0.5; + } + + let randomValue1: number; + + if (type === "Int") { + randomValue1 = Math.floor(Math.random() * 9999); + } else { + randomValue1 = Math.floor(Math.random() * 9999) + 0.5; + } + + let randomValue2: number; + + if (type === "Int") { + randomValue2 = Math.floor(Math.random() * 9999); + } else { + randomValue2 = Math.floor(Math.random() * 9999) + 0.5; + } + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { property_IN: [${value}, ${randomValue1}, ${randomValue2}] }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + + test("should find Movies LT number", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let value: number; + + if (type === "Int") { + value = Math.floor(Math.random() * 9999); + } else { + value = Math.floor(Math.random() * 9999) + 0.5; + } + + const lessThanValue = value - (value + 1); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $lessThanValue}) + `, + { value, lessThanValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_LT: ${lessThanValue + 1} }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(lessThanValue); + }); + + test("should find Movies LTE number", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let value: number; + + if (type === "Int") { + value = Math.floor(Math.random() * 9999); + } else { + value = Math.floor(Math.random() * 9999) + 0.5; + } + + const lessThanValue = value - (value + 1); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $lessThanValue}) + `, + { value, lessThanValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_LTE: ${value} }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + }); + + test("should find Movies GT number", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let value: number; + + if (type === "Int") { + value = Math.floor(Math.random() * 9999); + } else { + value = Math.floor(Math.random() * 9999) + 0.5; + } + + const graterThanValue = value + 1; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $graterThanValue}) + `, + { value, graterThanValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property_GT: ${graterThanValue - 1} }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(graterThanValue); + }); + + test("should find Movies GTE number", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: ${type} + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + let value: number; + + if (type === "Int") { + value = Math.floor(Math.random() * 9999); + } else { + value = Math.floor(Math.random() * 9999) + 0.5; + } + + const greaterThan = value + 1; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $greaterThan}) + `, + { value, greaterThan } + ); + + const query = ` + { + ${randomType.plural}(where: { property_GTE: ${value} }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + }); + }); + + describe("Boolean Filtering", () => { + test("should find Movies equality equality", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: Boolean + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = false; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { property_EQ: false }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + }); + + test("should find Movies NOT boolean", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + property: Boolean + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = false; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property_EQ: false } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(0); + }); + }); + + describe("Relationship/Connection Filtering", () => { + describe("equality", () => { + test("should find using relationship equality on node", async () => { + const randomType1 = testHelper.createUniqueType("Movie"); + const randomType2 = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${randomType1.name} @node { + id: ID + ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) + } + + type ${randomType2.name} @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const rootId = generate({ + charset: "alphabetic", + }); + + const relationId = generate({ + charset: "alphabetic", + }); + + const randomId = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (root:${randomType1.name} {id: $rootId}) + CREATE (:${randomType1.name} {id: $randomId}) + CREATE (relation:${randomType2.name} {id: $relationId}) + CREATE (:${randomType2.name} {id: $randomId}) + MERGE (root)-[:IN_GENRE]->(relation) + `, + { rootId, relationId, randomId } + ); + + const query = ` + { + ${randomType1.plural}(where: { ${randomType2.plural}_SOME: { id_EQ: "${relationId}" } }) { + id + ${randomType2.plural} { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType1.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType1.plural][0]).toMatchObject({ + id: rootId, + [randomType2.plural]: [{ id: relationId }], + }); + }); + + test("should find using equality on node using connection", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Genre = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${Movie} @node { + id: ID + genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT) + } + + type ${Genre} @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const movieId = generate({ + charset: "alphabetic", + }); + + const genreId = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${Movie} {id: $movieId})-[:IN_GENRE]->(:${Genre} {id:$genreId}) + `, + { movieId, genreId } + ); + + const query = ` + { + ${Movie.plural}(where: { genresConnection_SOME: { node: { id_EQ: "${genreId}" } } }) { + id + genres { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect(gqlResult.data as any).toEqual({ + [Movie.plural]: [ + { + id: movieId, + genres: [{ id: genreId }], + }, + ], + }); + }); + + test("should find using equality on relationship using connection", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Genre = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${Movie} @node { + id: ID + genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") + } + + type ${Genre} @node { + id: ID + } + + type ActedIn @relationshipProperties { + id: String + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const movieId = generate({ + charset: "alphabetic", + }); + + const genreId = generate({ + charset: "alphabetic", + }); + + const actedInId = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) + `, + { movieId, genreId, actedInId } + ); + + const query = ` + { + ${Movie.plural}(where: { genresConnection_SOME: { edge: { id_EQ: "${actedInId}" } } }) { + id + genres { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect(gqlResult.data as any).toEqual({ + [Movie.plural]: [ + { + id: movieId, + genres: [{ id: genreId }], + }, + ], + }); + }); + + test("should find relationship and node property equality using connection", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Genre = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${Movie} @node { + id: ID + genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") + } + + type ${Genre} @node { + id: ID + } + + type ActedIn @relationshipProperties { + id: String + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const movieId = generate({ + charset: "alphabetic", + }); + + const genreId = generate({ + charset: "alphabetic", + }); + + const actedInId = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) + `, + { movieId, genreId, actedInId } + ); + + const query = ` + { + ${Movie.plural}(where: { genresConnection_SOME: { node: { id_EQ: "${genreId}" } edge: { id_EQ: "${actedInId}" } } }) { + id + genres { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect(gqlResult.data as any).toEqual({ + [Movie.plural]: [ + { + id: movieId, + genres: [{ id: genreId }], + }, + ], + }); + }); + }); + + describe("NOT", () => { + test("should find using NOT on relationship", async () => { + const randomType1 = testHelper.createUniqueType("Movie"); + const randomType2 = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${randomType1.name} @node { + id: ID + ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) + } + + type ${randomType2.name} @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const rootId1 = generate({ + charset: "alphabetic", + }); + const rootId2 = generate({ + charset: "alphabetic", + }); + + const relationId1 = generate({ + charset: "alphabetic", + }); + const relationId2 = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (root1:${randomType1.name} {id: $rootId1}) + CREATE (root2:${randomType1.name} {id: $rootId2}) + CREATE (relation1:${randomType2.name} {id: $relationId1}) + CREATE (relation2:${randomType2.name} {id: $relationId2}) + MERGE (root1)-[:IN_GENRE]->(relation1) + MERGE (root2)-[:IN_GENRE]->(relation2) + `, + { rootId1, rootId2, relationId1, relationId2 } + ); + + const query = /* GraphQL */ ` + { + ${randomType1.plural}(where: { NOT: { ${randomType2.plural}_SOME: { id_EQ: "${relationId2}" } } }) { + id + ${randomType2.plural} { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType1.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType1.plural][0]).toMatchObject({ + id: rootId1, + [randomType2.plural]: [{ id: relationId1 }], + }); + }); + + test("should find using relationship properties and connections", async () => { + const randomType1 = testHelper.createUniqueType("Movie"); + const randomType2 = testHelper.createUniqueType("Genre"); + + const typeDefs = ` + type ${randomType1.name} @node { + id: ID + ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") + } + + type ${randomType2.name} @node { + id: ID + } + + type ActedIn @relationshipProperties { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const rootId1 = generate({ + charset: "alphabetic", + }); + const rootId2 = generate({ + charset: "alphabetic", + }); + + const relationId1 = generate({ + charset: "alphabetic", + }); + const relationId2 = generate({ + charset: "alphabetic", + }); + const actedInId = generate({ + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${randomType1.name} {id: $rootId1})-[:IN_GENRE {id: $actedInId}]->(:${randomType2.name} {id: $relationId1}) + CREATE (:${randomType1.name} {id: $rootId2})-[:IN_GENRE {id: randomUUID()}]->(:${randomType2.name} {id: $relationId2}) + `, + { rootId1, rootId2, relationId1, relationId2, actedInId } + ); + + const query = ` + { + ${randomType1.plural}(where: { ${randomType2.plural}Connection_NONE: { edge: { id_EQ: "${actedInId}" } } }) { + id + ${randomType2.plural} { + id + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + + expect((gqlResult.data as any)[randomType1.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType1.plural][0]).toMatchObject({ + id: rootId2, + [randomType2.plural]: [{ id: relationId2 }], + }); + }); + }); + + describe("List Predicates", () => { + let Movie: UniqueType; + let Actor: UniqueType; + + const movies = [ + ...Array(4) + .fill(null) + .map((_, i) => ({ id: generate(), budget: (i + 1) ** 2 })), + ]; + const actors = [ + ...Array(4) + .fill(null) + .map((_, i) => ({ id: generate(), flag: i % 2 === 0 })), + ]; + + beforeEach(async () => { + Movie = testHelper.createUniqueType("Movie"); + Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = ` + type ${Movie} @node { + id: ID! @id + budget: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + id: ID! @id + flag: Boolean! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m1:${Movie}) SET m1 = $movies[0] + CREATE (m2:${Movie}) SET m2 = $movies[1] + CREATE (m3:${Movie}) SET m3 = $movies[2] + CREATE (m4:${Movie}) SET m4 = $movies[3] + CREATE (a1:${Actor}) SET a1 = $actors[0] + CREATE (a2:${Actor}) SET a2 = $actors[1] + CREATE (a3:${Actor}) SET a3 = $actors[2] + CREATE (a4:${Actor}) SET a4 = $actors[3] + MERGE (a1)-[:ACTED_IN]->(m1)<-[:ACTED_IN]-(a3) + MERGE (a2)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(a3) + MERGE (a2)-[:ACTED_IN]->(m3)<-[:ACTED_IN]-(a4) + MERGE (a1)-[:ACTED_IN]->(m4)<-[:ACTED_IN]-(a2) + MERGE (a3)-[:ACTED_IN]->(m4) + `, + { movies, actors } + ); + }); + + describe("on relationship", () => { + function generateQuery(predicate: "ALL" | "NONE" | "SINGLE" | "SOME") { + return /* GraphQL */ ` + query($movieIds: [ID!]!) { + ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actors_${predicate}: { NOT: { flag_EQ: false } } }] }) { + id + actors(where: { NOT: { flag_EQ: false } }) { + id + flag + } + } + } + `; + } + + test("ALL", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + + test("NONE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[2]?.id, + actors: [], + }); + }); + + test("SINGLE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + }); + + test("SOME", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(3); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[3]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + }); + + describe("on relationship using NOT operator", () => { + const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => ` + query($movieIds: [ID!]!) { + ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actors_${predicate}: { NOT: { flag_EQ: false } } }] }) { + id + actors(where: { NOT: { flag_EQ: false } }) { + id + flag + } + } + } + `; + + test("ALL", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + + test("NONE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[2]?.id, + actors: [], + }); + }); + + test("SINGLE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + }); + + test("SOME", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(3); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[3]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + }); + + describe("on connection", () => { + const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => /* GraphQL */ ` + query($movieIds: [ID!]!) { + ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actorsConnection_${predicate}: { node: { NOT: { flag_EQ: false } } }}] }) { + id + actors(where: { NOT: { flag_EQ: false } }) { + id + flag + } + } + } + `; + + test("ALL", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + + test("NONE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[2]?.id, + actors: [], + }); + }); + + test("SINGLE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + }); + + test("SOME", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(3); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[3]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + }); + + describe("on connection using NOT operator", () => { + const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => ` + query($movieIds: [ID!]!) { + ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actorsConnection_${predicate}: { node: { NOT: { flag_EQ: false } } } }] }) { + id + actors(where: { NOT: { flag_EQ: false }}) { + id + flag + } + } + } + `; + + test("ALL", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + + test("NONE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[2]?.id, + actors: [], + }); + }); + + test("SINGLE", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(1); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + }); + + test("SOME", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + variableValues: { movieIds: movies.map(({ id }) => id) }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const gqlMovies = gqlResult.data?.[Movie.plural]; + + expect(gqlMovies).toHaveLength(3); + expect(gqlMovies).toContainEqual({ + id: movies[0]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[1]?.id, + actors: expect.toIncludeSameMembers([actors[2]]), + }); + expect(gqlMovies).toContainEqual({ + id: movies[3]?.id, + actors: expect.toIncludeSameMembers([actors[0], actors[2]]), + }); + }); + }); + }); + }); + + describe("NULL Filtering", () => { + // TODO: split in 2 tests + test("should work for existence and non-existence", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${randomType.name} @node { + id: String! + optional: String + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id1 = generate({ + readable: true, + charset: "alphabetic", + }); + + const id2 = generate({ + readable: true, + charset: "alphabetic", + }); + + const optionalValue = generate({ + readable: true, + charset: "alphabetic", + }); + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {id: $id1}) + CREATE (:${randomType.name} {id: $id2, optional: $optionalValue}) + `, + { id1, id2, optionalValue } + ); + + // Test NULL checking + + const nullQuery = ` + { + ${randomType.plural}(where: { optional_EQ: null }) { + id + } + } + `; + + const nullResult = await testHelper.executeGraphQL(nullQuery); + + expect(nullResult.errors).toBeUndefined(); + + expect((nullResult.data as any)[randomType.plural]).toHaveLength(1); + + expect((nullResult.data as any)[randomType.plural][0].id).toEqual(id1); + + // Test NOT NULL checking + + const notNullQuery = ` + { + ${randomType.plural}(where: { NOT: { optional_EQ: null } }) { + id + } + } + `; + + const notNullResult = await testHelper.executeGraphQL(notNullQuery); + + expect(notNullResult.errors).toBeUndefined(); + + expect((notNullResult.data as any)[randomType.plural]).toHaveLength(1); + + expect((notNullResult.data as any)[randomType.plural][0].id).toEqual(id2); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-auth-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-auth-deprecated.int.test.ts new file mode 100644 index 0000000000..0596ff5a83 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-auth-deprecated.int.test.ts @@ -0,0 +1,503 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createBearerToken } from "../../../../utils/create-bearer-token"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - Auth - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("With authorization on type", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(filter: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + return this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "goodbye" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + title + custom_field + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: [ + { + title: "The Matrix", + custom_field: "hello", + }, + ], + }); + }); + + test("With authorization on @cypher field, selecting @cypher field", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + @authorization(filter: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "goodbye" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + custom_field + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: [ + { + custom_field: "hello", + }, + ], + }); + }); + + test("With authorization on @cypher field, selecting title field", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + @authorization(filter: [{ where: { node: { title_EQ: "$jwt.custom_value" } } }]) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "goodbye" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + }, + { + title: "The Matrix Reloaded", + }, + ]), + }); + }); + + test("With authorization on Actor type field using nested Movie @cypher field return value", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node @authorization(filter: [{ where: { node: { movies_SOME: { custom_field_EQ: "$jwt.custom_value" } } } }]) { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "goodbye" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a2:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = ` + query { + ${Actor.plural} { + name + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Actor.plural]: [ + { + name: "Keanu Reeves", + }, + ], + }); + }); + + test("With authorization on a different field than the @cypher field", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String @authorization(filter: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "goodbye" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", custom_field: "hello" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + }, + { + title: "The Matrix Revolutions", + }, + ]), + }); + }); + + test("With authorization on type using @cypher return value, with validate FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "hello" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors?.[0]?.message).toBe("Forbidden"); + }); + + test("With authorization on type using @cypher return value, with validate PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken("secret", { custom_value: "hello" }); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", custom_field: "hello" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "hello" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", custom_field: "hello" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = ` + query { + ${Movie.plural} { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + }, + { + title: "The Matrix Reloaded", + }, + { + title: "The Matrix Revolutions", + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-list-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-list-deprecated.int.test.ts new file mode 100644 index 0000000000..0fe81bece0 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-list-deprecated.int.test.ts @@ -0,0 +1,424 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - List - deprecated", () => { + const testHelper = new TestHelper(); + let CustomType: UniqueType; + + beforeEach(() => { + CustomType = testHelper.createUniqueType("CustomType"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("String List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [String] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: ['a', 'b', 'c'] }) + CREATE (:${CustomType} { title: "test2", custom_data: ['d', 'e', 'f'] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "a" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Int List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [Int] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [1, 2, 3] }) + CREATE (:${CustomType} { title: "test2", custom_data: [4, 5, 6] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: 2 }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Float List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [Float] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [10.0, 20.0, 30.0] }) + CREATE (:${CustomType} { title: "test2", custom_data: [40.0, 50.0, 60.0] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: 20.0 }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Point List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [Point!] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [point({ latitude: 1, longitude: 2 }), point({ latitude: 3, longitude: 4 })] }) + CREATE (:${CustomType} { title: "test2", custom_data: [point({ latitude: 5, longitude: 6 }), point({ latitude: 7, longitude: 8 })] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: { latitude: 1, longitude: 2 } }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("CartesianPoint List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [CartesianPoint] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [point({ x: 1, y: 2, z: 3 }), point({ x: 3, y: 4, z: 5 })] }) + CREATE (:${CustomType} { title: "test2", custom_data: [point({ x: 5, y: 6, z: 7 }), point({ x: 7, y: 8, z: 9 })] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: { x: 1, y: 2, z: 3 } }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("DateTime List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [DateTime] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [datetime('2021-01-01T00:00:00Z'), datetime('2021-02-01T00:00:00Z')] }) + CREATE (:${CustomType} { title: "test2", custom_data: [datetime('2021-03-01T00:00:00Z'), datetime('2021-04-01T00:00:00Z')] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01T00:00:00Z" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("LocalDateTime List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [LocalDateTime] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [localdatetime('2021-01-01T00:00:00'), localdatetime('2021-02-01T00:00:00')] }) + CREATE (:${CustomType} { title: "test2", custom_data: [localdatetime('2021-03-01T00:00:00'), localdatetime('2021-04-01T00:00:00')] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01T00:00:00" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Date List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [Date] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [date('2021-01-01'), date('2021-02-01')] }) + CREATE (:${CustomType} { title: "test2", custom_data: [date('2021-03-01'), date('2021-04-01')] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Time List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [Time] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [time('12:00:00'), time('13:00:00')] }) + CREATE (:${CustomType} { title: "test2", custom_data: [time('14:00:00'), time('15:00:00')] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "12:00:00" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("LocalTime List cypher field: INCLUDES", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + custom_cypher_list: [LocalTime] @cypher(statement: + """ + MATCH (this) + RETURN this.custom_data as list + """ + , columnName: "list") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(` + CREATE (:${CustomType} { title: "test", custom_data: [localtime('12:00:00'), localtime('13:00:00')] }) + CREATE (:${CustomType} { title: "test2", custom_data: [localtime('14:00:00'), localtime('15:00:00')] }) + `); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "12:00:00" }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-one-to-one-relationship-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-one-to-one-relationship-deprecated.int.test.ts new file mode 100644 index 0000000000..67b1102f97 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-one-to-one-relationship-deprecated.int.test.ts @@ -0,0 +1,1169 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - One To One Relationship - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("1 to 1 relationship with single property filter", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actor: ${Actor}! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movie: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + actor: { + name_EQ: "Keanu Reeves" + } + } + ) { + title + actor { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + actor: { + name: "Keanu Reeves", + }, + }, + { + title: "The Matrix Reloaded", + actor: { + name: "Keanu Reeves", + }, + }, + { + title: "The Matrix Revolutions", + actor: { + name: "Keanu Reeves", + }, + }, + ]), + }); + }); + + test("1 to 1 relationship with single property filter with non-deterministic result", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actor: ${Actor}! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movie: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Actor.plural}( + where: { + movie: { + title_STARTS_WITH: "The Matrix" + } + } + ) { + name + movie { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Actor.plural]: expect.toIncludeSameMembers([ + { + name: "Keanu Reeves", + movie: { + // The result is non-deterministic, so we can potentially match any of the movies + title: expect.toStartWith("The Matrix"), + }, + }, + ]), + }); + }); + + test("1 to 1 relationship with null filter", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + actor: ${Actor} + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movie: ${Movie} + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", released: 2003 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + released_EQ: 2003, + actor: null + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Revolutions", + }, + ]), + }); + }); + + test("1 to 1 relationship with NOT null filter", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + actor: ${Actor} + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movie: ${Movie} + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", released: 2003 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a2:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { AND: [{ released_IN: [2003], NOT: { actor: null } }] } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + }, + { + title: "The Matrix Revolutions", + }, + ]), + }); + }); + + test("1 to 1 relationship with auth filter on type PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Person.plural]: [ + { + directed: { + title: "The Matrix", + directed_by: { + name: "Lilly Wachowski", + }, + }, + }, + ], + }); + }); + + test("1 to 1 relationship with auth filter on type FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Something Incorrect" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeTruthy(); + }); + + test("1 to 1 relationship with auth filter on field PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Person.plural]: [ + { + directed: { + title: "The Matrix", + directed_by: { + name: "Lilly Wachowski", + }, + }, + }, + ], + }); + }); + + test("1 to 1 relationship with auth filter on field FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Something Incorrect" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeTruthy(); + }); + + test("1 to 1 relationship with auth validate type PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Person.plural]: [ + { + directed: { + title: "The Matrix", + directed_by: { + name: "Lilly Wachowski", + }, + }, + }, + ], + }); + }); + + test("1 to 1 relationship with auth validate type FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Something Wrong" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors?.[0]?.message).toBe("Forbidden"); + }); + + test("1 to 1 relationship with auth validate field PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Person.plural]: [ + { + directed: { + title: "The Matrix", + directed_by: { + name: "Lilly Wachowski", + }, + }, + }, + ], + }); + }); + + test("1 to 1 relationship with auth validate field FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Something Wrong" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors?.[0]?.message).toBe("Forbidden"); + }); + + test("1 to 1 relationship with nested selection", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN) + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Person} { name: "Jada Pinkett Smith" }) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Person} { name: "Director Person" }) + CREATE (a3)-[:DIRECTED]->(m) + CREATE (a4:${Person} { name: "Lana Wachowski" }) + CREATE (a4)-[:DIRECTED]->(m2) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + actors { + name + movies { + directed_by { + name + } + title + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Person.plural]: [ + { + directed: { + title: "The Matrix", + directed_by: { + name: "Director Person", + }, + actors: expect.toIncludeSameMembers([ + { + name: "Keanu Reeves", + movies: expect.toIncludeSameMembers([ + { + directed_by: { + name: "Director Person", + }, + title: "The Matrix", + }, + { + directed_by: { + name: "Lana Wachowski", + }, + title: "The Matrix Reloaded", + }, + { + directed_by: { + name: "Lilly Wachowski", + }, + title: "The Matrix Revolutions", + }, + ]), + }, + ]), + }, + }, + ], + }); + }); + + test("1 to 1 relationship with connection", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Person = testHelper.createUniqueType("Person"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + released: Int + actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN) + directed_by: ${Person}! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:${Person}) + RETURN director + """ + columnName: "director" + ) + } + + type ${Person} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + directed: ${Movie}! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", released: 1999 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", released: 2003 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", released: 2003 }) + CREATE (a:${Person} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Person} { name: "Jada Pinkett Smith" }) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a5:${Person} { name: "Lilly Wachowski" }) + CREATE (a5)-[:DIRECTED]->(m) + CREATE (a5)-[:DIRECTED]->(m2) + CREATE (a5)-[:DIRECTED]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}(where: { directed_by: { name_EQ: "Lilly Wachowski"}, title_ENDS_WITH: "Matrix" }) { + actorsConnection { + totalCount + edges { + node { + name + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: [ + { + ["actorsConnection"]: { + totalCount: 1, + edges: [ + { + node: { + name: "Keanu Reeves", + }, + }, + ], + }, + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-scalar-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-scalar-deprecated.int.test.ts new file mode 100644 index 0000000000..06731599b9 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-scalar-deprecated.int.test.ts @@ -0,0 +1,288 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - Scalar - deprecated", () => { + let CustomType: UniqueType; + + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + beforeEach(() => { + CustomType = testHelper.createUniqueType("CustomType"); + }); + + test.each([ + { + title: "Int cypher field: exact match", + filter: `special_count_EQ: 1`, + }, + { + title: "Int cypher field: GT", + filter: `special_count_GT: 0`, + }, + { + title: "Int cypher field: GTE", + filter: `special_count_GTE: 1`, + }, + { + title: "Int cypher field: LT", + filter: `special_count_LT: 2`, + }, + { + title: "Int cypher field: LTE", + filter: `special_count_LTE: 2`, + }, + { + title: "Int cypher field: IN", + filter: `special_count_IN: [1, 2, 3]`, + }, + ] as const)("$title", async ({ filter }) => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:${CustomType}) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(`CREATE (m:${CustomType} { title: "test" })`, {}); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { ${filter} }) { + special_count + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + special_count: 1, + }, + ], + }); + }); + + test.each([ + { + title: "String cypher field: exact match", + filter: `special_word_EQ: "test"`, + }, + { + title: "String cypher field: CONTAINS", + filter: `special_word_CONTAINS: "es"`, + }, + { + title: "String cypher field: ENDS_WITH", + filter: `special_word_ENDS_WITH: "est"`, + }, + { + title: "String cypher field: STARTS_WITH", + filter: `special_word_STARTS_WITH: "tes"`, + }, + { + title: "String cypher field: IN", + filter: `special_word_IN: ["test", "test2"]`, + }, + ] as const)("$title", async ({ filter }) => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_word: String + @cypher( + statement: """ + RETURN "test" as s + """ + columnName: "s" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(`CREATE (m:${CustomType} { title: "test" })`, {}); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { ${filter} }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Int cypher field AND String title field", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:${CustomType}) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + UNWIND [ + {title: 'CustomType One' }, + {title: 'CustomType Two' }, + {title: 'CustomType Three' } + ] AS CustomTypeData + CREATE (m:${CustomType}) + SET m = CustomTypeData; + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { special_count_GTE: 1, title_EQ: "CustomType One" }) { + special_count + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + special_count: 3, + }, + ], + }); + }); + + test("unmatched Int cypher field AND String title field", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:${CustomType}) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + UNWIND [ + {title: 'CustomType One' }, + {title: 'CustomType Two' }, + {title: 'CustomType Three' } + ] AS CustomTypeData + CREATE (m:${CustomType}) + SET m = CustomTypeData; + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { special_count_GTE: 1, title_EQ: "CustomType Unknown" }) { + special_count + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [], + }); + }); + + test("Int cypher field, selecting String title field", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:${CustomType}) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher(`CREATE (m:${CustomType} { title: "test" })`, {}); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}(where: { special_count_GTE: 1 }) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-sorting-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-sorting-deprecated.int.test.ts new file mode 100644 index 0000000000..129d510699 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-sorting-deprecated.int.test.ts @@ -0,0 +1,200 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - Sorting - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("With sorting on the return value", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + custom_field: String + @cypher( + statement: """ + WITH this + RETURN this.title AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + await testHelper.executeCypher( + ` + CREATE (m1:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The" }) + CREATE (a1:${Actor} { name: "Keanu Reeves" }) + CREATE (a2:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a1)-[:ACTED_IN]->(m1) + CREATE (a1)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a1)-[:ACTED_IN]->(m3) + CREATE (a2)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { custom_field_STARTS_WITH: "The Matrix" } + sort: [{ custom_field: DESC }] + ) { + title + actors { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + actors: expect.toIncludeSameMembers([ + { + name: "Keanu Reeves", + }, + { + name: "Jada Pinkett Smith", + }, + ]), + }, + { + title: "The Matrix", + actors: [ + { + name: "Keanu Reeves", + }, + ], + }, + ]), + }); + }); + + test("With sorting on the return value of a different field", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field AS s + """ + columnName: "s" + ) + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + await testHelper.executeCypher( + ` + CREATE (m1:${Movie} { title: "The Matrix", custom_field: "hello world!" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", custom_field: "hello world!" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", custom_field: "goodbye world!" }) + CREATE (a1:${Actor} { name: "Keanu Reeves" }) + CREATE (a2:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a1)-[:ACTED_IN]->(m1) + CREATE (a1)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a1)-[:ACTED_IN]->(m3) + CREATE (a2)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { custom_field_EQ: "hello world!" } + sort: [{ title: DESC }] + ) { + title + actors { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + actors: expect.toIncludeSameMembers([ + { + name: "Keanu Reeves", + }, + { + name: "Jada Pinkett Smith", + }, + ]), + }, + { + title: "The Matrix", + actors: [ + { + name: "Keanu Reeves", + }, + ], + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-temporal-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-temporal-deprecated.int.test.ts new file mode 100644 index 0000000000..7ccea12469 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/cypher-filtering-temporal-deprecated.int.test.ts @@ -0,0 +1,346 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("cypher directive filtering - Temporal - deprecated", () => { + let CustomType: UniqueType; + + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + beforeEach(() => { + CustomType = testHelper.createUniqueType("CustomType"); + }); + + test("DateTime cypher field", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_time: DateTime + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS t + """ + columnName: "t" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: datetime("2024-09-03T15:30:00Z") }) + CREATE (:${CustomType} { title: "test2", custom_data: datetime("2025-09-03T15:30:00Z") }) + CREATE (:${CustomType} { title: "test3", custom_data: datetime("2023-09-03T15:30:00Z") }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_time_GT: "2024-09-02T00:00:00Z" + } + ) { + special_time + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: expect.toIncludeSameMembers([ + { + special_time: "2024-09-03T15:30:00.000Z", + title: "test", + }, + { + special_time: "2025-09-03T15:30:00.000Z", + title: "test2", + }, + ]), + }); + }); + + test("Duration cypher field", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_duration: Duration + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS d + """ + columnName: "d" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: duration("P14DT16H12M") }) + CREATE (:${CustomType} { title: "test2", custom_data: duration("P14DT16H13M") }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_duration_EQ: "P14DT16H12M" + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: [ + { + title: "test", + }, + ], + }); + }); + + test("Duration cypher field LT", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_duration: Duration + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS d + """ + columnName: "d" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: duration('P14DT16H12M') }) + CREATE (:${CustomType} { title: "test2", custom_data: duration('P14DT16H13M') }) + CREATE (:${CustomType} { title: "test3", custom_data: duration('P13DT16H13M') }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_duration_LT: "P14DT16H13M" + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: expect.toIncludeSameMembers([ + { + title: "test", + }, + { + title: "test3", + }, + ]), + }); + }); + + test("Duration cypher field LTE", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_duration: Duration + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS d + """ + columnName: "d" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: duration('P14DT16H12M') }) + CREATE (:${CustomType} { title: "test2", custom_data: duration('P14DT16H13M') }) + CREATE (:${CustomType} { title: "test3", custom_data: duration('P13DT16H13M') }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_duration_LTE: "P14DT16H12M" + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: expect.toIncludeSameMembers([ + { + title: "test", + }, + { + title: "test3", + }, + ]), + }); + }); + + test("Duration cypher field GT", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_duration: Duration + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS d + """ + columnName: "d" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: duration('P14DT16H12M') }) + CREATE (:${CustomType} { title: "test2", custom_data: duration('P14DT16H13M') }) + CREATE (:${CustomType} { title: "test3", custom_data: duration('P13DT16H13M') }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_duration_GT: "P14DT16H11M" + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: expect.toIncludeSameMembers([ + { + title: "test", + }, + { + title: "test2", + }, + ]), + }); + }); + + test("Duration cypher field GTE", async () => { + const typeDefs = /* GraphQL */ ` + type ${CustomType} @node { + title: String + special_duration: Duration + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_data AS d + """ + columnName: "d" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (:${CustomType} { title: "test", custom_data: duration('P14DT16H12M') }) + CREATE (:${CustomType} { title: "test2", custom_data: duration('P14DT16H13M') }) + CREATE (:${CustomType} { title: "test3", custom_data: duration('P13DT16H13M') }) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${CustomType.plural}( + where: { + special_duration_GTE: "P14DT16H12M" + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [CustomType.plural]: expect.toIncludeSameMembers([ + { + title: "test", + }, + { + title: "test2", + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-auth-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-auth-deprecated.int.test.ts new file mode 100644 index 0000000000..1cb9b22978 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-auth-deprecated.int.test.ts @@ -0,0 +1,384 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("cypher directive filtering - relationship auth filter -deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("relationship with auth filter on type PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + rating: Float + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Jada Pinkett Smith" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", rating: 10.0 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", rating: 8.0 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", rating: 6.0 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + rating_GT: 7.0 + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix Reloaded", + }, + }, + ]), + }, + }); + }); + + test("relationship with auth filter on type FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + rating: Float + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Something Incorrect" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", rating: 10.0 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", rating: 8.0 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", rating: 6.0 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + rating_GT: 7.0 + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: [], + }, + }); + }); + + test("relationship with auth validate on type PASS", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + rating: Float + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Jada Pinkett Smith" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", rating: 10.0 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", rating: 8.0 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", rating: 6.0 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + rating_LT: 7.0 + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix Revolutions", + }, + }, + ]), + }, + }); + }); + + test("relationship with auth validate on type FAIL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + rating: Float + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = testHelper.createBearerToken("secret", { custom_value: "Jada Pinkett Smith" }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix", rating: 10.0 }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded", rating: 8.0 }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions", rating: 6.0 }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + rating_GT: 7.0 + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors?.[0]?.message).toBe("Forbidden"); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-connection-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-connection-deprecated.int.test.ts new file mode 100644 index 0000000000..ce254f7f24 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-connection-deprecated.int.test.ts @@ -0,0 +1,630 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("Connection API - cypher directive filtering - Relationship - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("Connection API - relationship with single property filter NOT", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + NOT: { + actors_SOME: { + name_EQ: "Jada Pinkett Smith" + } + } + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { title: "The Matrix" }, + }, + ]), + }, + }); + }); + + test("Connection API - relationship with single property filter ALL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + actors_ALL: { + name_EQ: "Keanu Reeves" + } + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix Reloaded", + }, + }, + ]), + }, + }); + }); + + test("Connection API - relationship with single property filter SINGLE", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a3:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a3)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + actors_SINGLE: { + name_EQ: "Carrie-Anne Moss" + } + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix Reloaded", + }, + }, + ]), + }, + }); + }); + + test("Connection API - relationship with single property filter SOME", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + actors_SOME: { + name_EQ: "Keanu Reeves" + } + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix", + }, + }, + { + node: { + title: "The Matrix Reloaded", + }, + }, + ]), + }, + }); + }); + + test("Connection API - relationship with single property filter SOME with sort", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + actors_SOME: { + name_EQ: "Keanu Reeves" + } + } + sort: { + title: DESC + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: [ + { + node: { + title: "The Matrix Reloaded", + }, + }, + { + node: { + title: "The Matrix", + }, + }, + ], + }, + }); + }); + + test("Connection API - relationship with single property filter NONE", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + actors_NONE: { + name_EQ: "Keanu Reeves" + } + } + ) { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "The Matrix Reloaded", + }, + }, + ]), + }, + }); + }); + + test("relationship with multiple property filters", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + const Genre = testHelper.createUniqueType("Genre"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + genres: [${Genre}!]! + @cypher( + statement: """ + MATCH (this)-[:IN_GENRE]->(g:${Genre}) + RETURN g + """ + columnName: "g" + ) + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + + type ${Genre} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)<-[:IN_GENRE]-(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (g:${Genre} { name: "Action" }) + CREATE (g2:${Genre} { name: "Romance" }) + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (m4:${Movie} { title: "A different movie" }) + CREATE (m)-[:IN_GENRE]->(g) + CREATE (m2)-[:IN_GENRE]->(g) + CREATE (m3)-[:IN_GENRE]->(g) + CREATE (m4)-[:IN_GENRE]->(g2) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.operations.connection}( + where: { + OR: [ + { actors_SOME: { name_EQ: "Jada Pinkett Smith" } }, + { genres_SOME: { name_EQ: "Romance" } } + ] + } + ) + { + edges { + node { + title + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { node: { title: "A different movie" } }, + { node: { title: "The Matrix Reloaded" } }, + { node: { title: "The Matrix Revolutions" } }, + ]), + }, + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-deprecated.int.test.ts new file mode 100644 index 0000000000..7f93ca9608 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/cypher/relationships/cypher-filtering-relationship-deprecated.int.test.ts @@ -0,0 +1,504 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../../../../utils/tests-helper"; + +describe("cypher directive filtering - Relationship - deprecated", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + test("relationship with single property filter NOT", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + NOT: { + actors_SOME: { + name_EQ: "Jada Pinkett Smith" + } + } + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + }, + ]), + }); + }); + + test("relationship with single property filter ALL", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + actors_ALL: { + name_EQ: "Keanu Reeves" + } + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + }, + ]), + }); + }); + + test("relationship with single property filter SINGLE", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a3:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a3)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + actors_SINGLE: { + name_EQ: "Carrie-Anne Moss" + } + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + }, + ]), + }); + }); + + test("relationship with single property filter SOME", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + actors_SOME: { + name_EQ: "Keanu Reeves" + } + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix", + }, + { + title: "The Matrix Reloaded", + }, + ]), + }); + }); + + test("relationship with single property filter NONE", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m2) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + actors_NONE: { + name_EQ: "Keanu Reeves" + } + } + ) { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "The Matrix Reloaded", + }, + ]), + }); + }); + + test("relationship with multiple property filters", async () => { + const Movie = testHelper.createUniqueType("Movie"); + const Actor = testHelper.createUniqueType("Actor"); + const Genre = testHelper.createUniqueType("Genre"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String + genres: [${Genre}!]! + @cypher( + statement: """ + MATCH (this)-[:IN_GENRE]->(g:${Genre}) + RETURN g + """ + columnName: "g" + ) + actors: [${Actor}!]! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:${Actor}) + RETURN actor + """ + columnName: "actor" + ) + } + + type ${Actor} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + + type ${Genre} @node { + name: String + movies: [${Movie}!]! + @cypher( + statement: """ + MATCH (this)<-[:IN_GENRE]-(movie:${Movie}) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + await testHelper.executeCypher( + ` + CREATE (g:${Genre} { name: "Action" }) + CREATE (g2:${Genre} { name: "Romance" }) + CREATE (m:${Movie} { title: "The Matrix" }) + CREATE (m2:${Movie} { title: "The Matrix Reloaded" }) + CREATE (m3:${Movie} { title: "The Matrix Revolutions" }) + CREATE (m4:${Movie} { title: "A different movie" }) + CREATE (m)-[:IN_GENRE]->(g) + CREATE (m2)-[:IN_GENRE]->(g) + CREATE (m3)-[:IN_GENRE]->(g) + CREATE (m4)-[:IN_GENRE]->(g2) + CREATE (a:${Actor} { name: "Keanu Reeves" }) + CREATE (a)-[:ACTED_IN]->(m) + CREATE (a)-[:ACTED_IN]->(m2) + CREATE (a)-[:ACTED_IN]->(m3) + CREATE (a2:${Actor} { name: "Carrie-Anne Moss" }) + CREATE (a2)-[:ACTED_IN]->(m) + CREATE (a2)-[:ACTED_IN]->(m2) + CREATE (a2)-[:ACTED_IN]->(m3) + CREATE (a3:${Actor} { name: "Jada Pinkett Smith" }) + CREATE (a3)-[:ACTED_IN]->(m2) + CREATE (a3)-[:ACTED_IN]->(m3) + `, + {} + ); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}( + where: { + OR: [ + { actors_SOME: { name_EQ: "Jada Pinkett Smith" } }, + { genres_SOME: { name_EQ: "Romance" } } + ] + } + ) + { + title + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult?.data).toEqual({ + [Movie.plural]: expect.toIncludeSameMembers([ + { + title: "A different movie", + }, + { + title: "The Matrix Reloaded", + }, + { + title: "The Matrix Revolutions", + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/interface-filtering-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/interface-filtering-deprecated.int.test.ts new file mode 100644 index 0000000000..d2efcaa7fc --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/interface-filtering-deprecated.int.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("Interface filtering - deprecated", () => { + const secret = "the-secret"; + + const testHelper = new TestHelper(); + let typeDefs: string; + + const Movie = testHelper.createUniqueType("Movie"); + const Series = testHelper.createUniqueType("Series"); + const Actor = testHelper.createUniqueType("Actor"); + + beforeAll(async () => { + typeDefs = /* GraphQL */ ` + interface Show { + title: String! + actors: [${Actor}!]! @declareRelationship + } + + type ${Movie} implements Show @limit(default: 3, max: 10) @node { + title: String! + cost: Float + runtime: Int + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${Series} implements Show @node { + title: String! + episodes: Int + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${Actor} @node { + name: String! + actedIn: [Show!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int + } + `; + + await testHelper.executeCypher(` + CREATE(m:${Movie} { title: "The Office" }) + CREATE(m2:${Movie}{ title: "The Office 2" }) + CREATE(m3:${Movie}{ title: "NOT The Office 2" }) + CREATE(s1:${Series}{ title: "The Office 2" }) + CREATE(s2:${Series}{ title: "NOT The Office" }) + CREATE(a:${Actor} {name: "Keanu"}) + MERGE(a)-[:ACTED_IN]->(m) + MERGE(a)-[:ACTED_IN]->(m2) + MERGE(a)-[:ACTED_IN]->(m3) + MERGE(a)-[:ACTED_IN]->(s1) + MERGE(a)-[:ACTED_IN]->(s2) + + `); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + afterAll(async () => { + await testHelper.close(); + }); + + test("allow for logical filters on top-level interfaces", async () => { + const query = ` + query actedInWhere { + shows(where: { OR: [{ title_EQ: "The Office" }, { title_EQ: "The Office 2" }] }) { + title + } + } + `; + + const token = createBearerToken(secret, {}); + const queryResult = await testHelper.executeGraphQLWithToken(query, token); + expect(queryResult.errors).toBeUndefined(); + expect(queryResult.data).toEqual({ + shows: expect.toIncludeSameMembers([ + { + title: "The Office", + }, + { + title: "The Office 2", + }, + { + title: "The Office 2", + }, + ]), + }); + }); + + test("allow for logical filters on nested-level interfaces", async () => { + const query = ` + query actedInWhere { + ${Actor.plural} { + actedIn(where: { OR: [{ title_EQ: "The Office" }, { title_EQ: "The Office 2" }] }) { + title + } + } + } + `; + + const token = createBearerToken(secret, {}); + const queryResult = await testHelper.executeGraphQLWithToken(query, token); + expect(queryResult.errors).toBeUndefined(); + expect(queryResult.data).toEqual({ + [Actor.plural]: [ + { + actedIn: expect.toIncludeSameMembers([ + { + title: "The Office", + }, + { + title: "The Office 2", + }, + { + title: "The Office 2", + }, + ]), + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/is-authenticated-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/is-authenticated-deprecated.int.test.ts new file mode 100644 index 0000000000..ccc966cc0d --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/is-authenticated-deprecated.int.test.ts @@ -0,0 +1,3395 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IncomingMessage } from "http"; +import { Socket } from "net"; +import { generate } from "randomstring"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("auth/is-authenticated deprecated", () => { + const testHelper = new TestHelper(); + + let Product: UniqueType; + let User: UniqueType; + let Post: UniqueType; + + const secret = "secret"; + + beforeEach(async () => { + Product = testHelper.createUniqueType("Product"); + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + await testHelper.executeCypher( + `CREATE(p:${Product} {id: "1", name: "Marvin"}) + CREATE(u:${User} {id: "1", password: "dontpanic42", name: "Arthur"}) + ` + ); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("read", () => { + test("should throw if not authenticated type definition", async () => { + const typeDefs = ` + type ${Product} @authentication(operations: [READ]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + { + ${Product.plural} { + id + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const typeDefs = ` + type ${Product} @authentication(operations: [READ]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + { + ${Product.plural} { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + type ${Product} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + { + ${Product.plural} { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if authenticated with incorrect role type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + type ${Product} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + { + ${Product.plural} { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated on field definition", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + password: String @authentication(operations: [READ]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + { + ${User.plural} { + password + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("create", () => { + test("should not throw if authenticated on type definition", async () => { + const typeDefs = ` + type ${User} @authentication(operations: [CREATE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @authentication(operations: [CREATE], jwt: {roles_INCLUDES: "admin"}) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on type definition", async () => { + const typeDefs = ` + type ${User} @authentication(operations: [CREATE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + type ${User} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated on nested create type", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @authentication(operations: [CREATE]) @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on nested create type", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on nested create type - not unwind-create", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated on field definition", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + password: String @authentication(operations: [CREATE]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ password: "1" }]) { + ${User.plural} { + password + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + test("should not throw if authenticated with correct role on field definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + password: String @authentication(operations: [CREATE], jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ password: "1" }]) { + ${User.plural} { + password + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on field definition", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + password: String @authentication(operations: [CREATE]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ password: "1" }]) { + ${User.plural} { + password + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on field definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + password: String @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ password: "1" }]) { + ${User.plural} { + password + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on field definition - not unwind-create", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + password: String @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ password: "1" }]) { + ${User.plural} { + password + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated on nested create field", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @node { + id: ID @authentication(operations: [CREATE]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on nested create field", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @node { + id: ID @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on nested create field - not unwind-create", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) + } + + type ${Product} @node { + id: ID @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = ` + mutation { + ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("update", () => { + test("should not throw if authenticated on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @authentication(operations: [UPDATE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @authentication(operations: [UPDATE], jwt: {roles_INCLUDES: "admin"}) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @authentication(operations: [UPDATE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + type ${User} @authentication(operations: [UPDATE], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + password: String @authentication(operations: [UPDATE]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + password: String @authentication(operations: [UPDATE], jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + password: String @authentication(operations: [UPDATE]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { password_SET: "1" }) { + ${User.plural} { + password + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + type ${User} @node { + id: ID + password: String @authentication(operations: [UPDATE], jwt: { roles_INCLUDES: "admin" }) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { password_SET: "1" }) { + ${User.plural} { + password + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("connect", () => { + test("should not throw if authenticated", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [CREATE_RELATIONSHIP]) + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [CREATE_RELATIONSHIP]) + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + // missing super-admin + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + // missing super-admin + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("disconnect", () => { + test("should not throw if authenticated", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphLQ */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [DELETE_RELATIONSHIP]) + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphLQ */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [DELETE_RELATIONSHIP]) + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + // missing super-admin + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + // missing super-admin + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} + @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles at nested level", async () => { + const Post = testHelper.createUniqueType("Post"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("delete", () => { + test("should not throw if authenticated on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @authentication(operations: [DELETE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @authentication(operations: [DELETE], jwt: {roles_INCLUDES: "admin"}) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @authentication(operations: [DELETE]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated on type definition (with nested delete)", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node @authentication(operations: [DELETE]) { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{ posts: {where:{node: { id_EQ: "${postId}"}}} }) { + nodesDeleted + } + } + `; + + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:Post {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @authentication(operations: [DELETE], jwt: { roles_INCLUDES: "admin" }) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect roles on type definition (with nested delete)", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node @authentication(operations: [DELETE], jwt: { roles_INCLUDES: "admin" }) { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{posts: {where:{node: { id_EQ: "${postId}"}}} }) { + nodesDeleted + } + } + `; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:Post {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if not authenticated on type definition (with nested delete) on field", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node @authentication(operations: [DELETE]) { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{ posts: {where:{node: { id_EQ: "${postId}"}}} }) { + nodesDeleted + } + } + `; + + const token = "not valid token"; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:${Post} {id: "${postId}"}) + `); + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("custom-resolvers", () => { + test("should not throw if authenticated on custom Query with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @mutation(operations: []) @query(read: false, aggregate: false) @node { + id: ID + name: String + } + + type Query { + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + query { + users { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct roles on custom Query with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @mutation(operations: []) @query(read: false, aggregate: false) @node { + id: ID + name: String + } + + type Query { + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + query { + users { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on custom Query with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @mutation(operations: []) @query(read: false, aggregate: false) @node { + id: ID + name: String + } + + type Query { + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + query { + users { + id + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on custom Query with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @mutation(operations: []) @query(read: false, aggregate: false) @node { + id: ID + name: String + } + + type Query { + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + query { + users { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated on custom Mutation with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + } + + type Mutation { + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + createUser { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on custom Mutation with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + } + + type Mutation { + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + createUser { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on custom Mutation with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + } + + type Mutation { + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + createUser { + id + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on custom Mutation with @cypher", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + name: String + } + + type Mutation { + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + mutation { + createUser { + id + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated on Field definition @cypher", async () => { + const History = testHelper.createUniqueType("History"); + + const typeDefs = /* GraphQL */ ` + type ${History} @node { + url: String + } + + type ${User} @node { + id: ID + history: [${History}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") + @authentication(operations: [READ]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${User.plural} { + history { + url + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should not throw if authenticated with correct role on Field definition @cypher", async () => { + const History = testHelper.createUniqueType("History"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${History} @node { + url: String + } + + type ${User} @node { + id: ID + history: [${History}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") + @authentication(operations: [READ], jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${User.plural} { + history { + url + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + + test("should throw if not authenticated on Field definition @cypher", async () => { + const History = testHelper.createUniqueType("History"); + + const typeDefs = /* GraphQL */ ` + type ${History} @node { + url: String + } + + type ${User} @node { + id: ID + history: [${History}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") + @authentication(operations: [READ]) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${User.plural} { + history { + url + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if authenticated with incorrect role on Field definition @cypher", async () => { + const History = testHelper.createUniqueType("History"); + + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${History} @node { + url: String + } + + type ${User} @node { + id: ID + history: [${History}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") + @authentication(operations: [READ], jwt: {roles_INCLUDES: "admin"}) + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${User.plural} { + history { + url + } + } + } + `; + + const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if decoded JWT passed in context", async () => { + const typeDefs = /* GraphQL */ ` + type ${Product} @authentication(operations: [READ]) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${Product.plural} { + id + } + } + `; + + const jwt = { + sub: "1234567890", + name: "John Doe", + iat: 1516239022, + }; + + const gqlResult = await testHelper.executeGraphQL(query, { + contextValue: { jwt }, + }); + + expect(gqlResult.errors).toBeFalsy(); + }); + + test("should not throw if decoded JWT passed in context matches claim", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + name: String! + } + + type ${Product} @authentication(operations: [READ], jwt: {name_STARTS_WITH: "John"}) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${Product.plural} { + id + } + } + `; + + const jwt = { + sub: "1234567890", + name: "John Doe", + iat: 1516239022, + }; + + const gqlResult = await testHelper.executeGraphQL(query, { + contextValue: { jwt }, + }); + + expect(gqlResult.errors).toBeFalsy(); + }); + + test("should throw if decoded JWT passed in context does not matches claim", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + name: String! + } + + type ${Product} @authentication(operations: [READ], jwt: {name_STARTS_WITH: "Doe"}) @node { + id: ID + name: String + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + const query = /* GraphQL */ ` + { + ${Product.plural} { + id + } + } + `; + + const jwt = { + sub: "1234567890", + name: "John Doe", + iat: 1516239022, + }; + + const gqlResult = await testHelper.executeGraphQL(query, { + contextValue: { jwt }, + }); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + }); + + describe("schema", () => { + describe("read", () => { + beforeEach(async () => { + const typeDefs = /* GraphQL */ ` + type ${Product} @node { + id: ID + name: String + } + extend schema @authentication(operations: [READ]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const query = /* GraphQL */ ` + { + ${Product.plural} { + id + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const query = /* GraphQL */ ` + { + ${Product.plural} { + id + } + } + `; + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + describe("create", () => { + beforeEach(async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + } + extend schema @authentication(operations: [CREATE]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const query = /* GraphQL */ ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const query = /* GraphQL */ ` + mutation { + ${User.operations.create}(input: [{ id: "1" }]) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + describe("update", () => { + beforeEach(async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + name: String + } + extend schema @authentication(operations: [UPDATE]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const query = ` + mutation { + ${User.operations.update}(update: { id_SET: "1" }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + describe("connect", () => { + let Post: UniqueType; + + beforeEach(async () => { + Post = testHelper.createUniqueType("Post"); + + const typeDefs = ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + extend schema @authentication(operations: [CREATE_RELATIONSHIP]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + + describe("disconnect", () => { + let Post: UniqueType; + + beforeEach(async () => { + Post = testHelper.createUniqueType("Post"); + + const typeDefs = ` + type ${Post} @node { + id: String + content: String + } + + type ${User} @node { + id: ID + name: String + password: String + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + extend schema @authentication(operations: [DELETE_RELATIONSHIP]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + } + } + } + `; + + const token = createBearerToken(secret); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + describe("delete", () => { + beforeEach(async () => { + const typeDefs = ` + type ${User} @node { + id: ID + name: String + } + extend schema @authentication(operations: [DELETE]) + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const query = ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const query = ` + mutation { + ${User.operations.delete} { + nodesDeleted + } + } + `; + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + describe("custom-resolvers", () => { + beforeEach(async () => { + const typeDefs = ` + type ${User} @node { + id: ID + name: String + } + type Query { + allUsers: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") + } + extend schema @authentication + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("should throw if not authenticated type definition", async () => { + const query = ` + query { + allUsers { + id + } + } + `; + + const token = "not valid token"; + + const socket = new Socket({ readable: true }); + const req = new IncomingMessage(socket); + req.headers.authorization = `Bearer ${token}`; + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should not throw if authenticated type definition", async () => { + const query = ` + query { + allUsers { + id + } + } + `; + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + }); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/object-path-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/object-path-deprecated.int.test.ts new file mode 100644 index 0000000000..b74fd55475 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/object-path-deprecated.int.test.ts @@ -0,0 +1,250 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("auth/object-path - deprecated", () => { + const testHelper = new TestHelper(); + const secret = "secret"; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(() => { + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should use object path with allow", async () => { + const typeDefs = ` + type JWTPayload @jwt { + nestedSub: String! @jwtClaim(path: "nested.object.path.sub") + } + + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { id_EQ: "$jwt.nestedSub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural}(where: {id_EQ: "${userId}"}) { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + `); + + const token = createBearerToken(secret, { + nested: { + object: { + path: { + sub: userId, + }, + }, + }, + }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const [user] = (gqlResult.data as any)[User.plural]; + expect(user).toEqual({ id: userId }); + }); + + test("should use $context value plucking on auth", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) + } + + extend type ${Post} @node @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$context.userId" } } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${Post.plural}(where: {id_EQ: "${postId}"}) { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQL(query, { + contextValue: { token, userId }, + }); + + expect(gqlResult.errors).toBeUndefined(); + + const [post] = (gqlResult.data as any)[Post.plural]; + expect(post).toEqual({ id: postId }); + }); + + test("should use object path with roles", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! @jwtClaim(path: "https://github\\\\.com/claims.https://github\\\\.com/claims/roles") + } + + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural}(where: {id_EQ: "${userId}"}) { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + `); + + const token = createBearerToken(secret, { + "https://github.com/claims": { "https://github.com/claims/roles": ["admin"] }, + }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const [user] = (gqlResult.data as any)[User.plural]; + + expect(user).toEqual({ id: userId }); + }); + + test("should use object path with JWT endpoint", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural}(where: {id_EQ: "${userId}"}) { + id + } + } + `; + + // Pass the well-known JWKS Endpoint + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: { + url: "https://www.YOUR_DOMAIN.com/.well-known/jwks.json", + }, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + `); + + // Not a valid JWT since signature shall never match + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + // Since we don't have a valid JWKS Endpoint, we will always get an error validating our JWKS + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/roles-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/roles-deprecated.int.test.ts new file mode 100644 index 0000000000..f9ba82cda9 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/roles-deprecated.int.test.ts @@ -0,0 +1,1002 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("auth/roles - deprecated", () => { + const testHelper = new TestHelper(); + const secret = "secret"; + + let typeUser: UniqueType; + let typeProduct: UniqueType; + let typePost: UniqueType; + let typeHistory: UniqueType; + + beforeEach(async () => { + typeUser = testHelper.createUniqueType("User"); + typeProduct = testHelper.createUniqueType("Product"); + typePost = testHelper.createUniqueType("Post"); + typeHistory = testHelper.createUniqueType("History"); + + await testHelper.executeCypher( + ` + CREATE (:${typeProduct} { name: 'p1', id:123 }) + CREATE (:${typeUser} { id: 1234, password:'dontpanic' }) + ` + ); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("read", () => { + test("should throw if missing role on type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeProduct} @authorization(validate: [{ + when: [BEFORE], + operations: [READ], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) @node { + id: ID + name: String + } + `; + + const query = ` + { + ${typeProduct.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("should throw if missing role on field definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + password: String @authorization(validate: [{ + when: [BEFORE], + operations: [READ], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + } + `; + + const query = ` + { + ${typeUser.plural} { + password + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("Read Node & Cypher Field", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeHistory} @node { + url: String @authorization(validate: [{ + when: [BEFORE], + operations: [READ], + where: { jwt: { roles_INCLUDES: "super-admin" } } + }]) + } + type ${typeUser} @node { + id: ID + name: String + password: String + } + + extend type ${typeUser} + @authorization(validate: [{ + when: [BEFORE], + operations: [READ, CREATE, UPDATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, DELETE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + + extend type ${typeUser} { + history: [${typeHistory}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${typeHistory}) RETURN h", columnName: "h") + @authorization(validate: [{ + when: [BEFORE], + operations: [READ], + where: { jwt: { roles_INCLUDES: "super-admin" } } + }]) + } + `; + + const query = ` + { + ${typeUser.plural} { + history { + url + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + MATCH (u:${typeUser} { id: 1234 }) + CREATE(h:${typeHistory} { url: 'http://some.url' })<-[:HAS_HISTORY]-(u) + `); + + const token = createBearerToken(secret, { sub: "super_admin", roles: ["admin", "super-admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + expect(gqlResult.data).toEqual({ + [typeUser.plural]: [ + { + history: [ + { + url: "http://some.url", + }, + ], + }, + ], + }); + }); + + // This tests reproduces the security issue related to authorization without match #195 + // eslint-disable-next-line jest/no-disabled-tests + test.skip("should throw if missing role on type definition and no nodes are matched", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type NotANode @authorization(validate: [{ + when: [BEFORE], + operations: [READ], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) { + name: String + } + `; + + const query = ` + { + notANodes { + name + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + describe("create", () => { + test("should throw if missing role on type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @authorization(validate: [{ + when: [AFTER], + operations: [CREATE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) @node { + id: ID + name: String + } + `; + + const query = ` + mutation { + ${typeUser.operations.create}(input: [{ id: "1" }]) { + ${typeUser.plural} { + id + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("should throw if missing role on field definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + password: String @authorization(validate: [{ + when: [AFTER], + operations: [CREATE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + } + `; + + const query = ` + mutation { + ${typeUser.operations.create}(input: [{ password: "1" }]) { + ${typeUser.plural} { + password + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("should not throw if missing role on field definition if is not specified in the request", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + password: String @authorization(validate: [{ + when: [AFTER], + operations: [CREATE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + } + `; + + const query = ` + mutation { + ${typeUser.operations.create}(input: [{ id: "1" }]) { + ${typeUser.plural} { + password + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeFalsy(); + }); + }); + + describe("update", () => { + test("should throw if missing role on type definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @authorization(validate: [{ + when: [BEFORE], + operations: [UPDATE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) @node{ + id: ID + name: String + } + `; + + const query = /* GraphQL */ ` + mutation { + ${typeUser.operations.update}(update: { id_SET: "1" }) { + ${typeUser.plural} { + id + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("should throw if missing role on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + password: String @authorization(validate: [{ + when: [BEFORE], + operations: [UPDATE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + } + `; + + const query = /* GraphQL */ ` + mutation { + ${typeUser.operations.update}(update: { password_SET: "1" }) { + ${typeUser.plural} { + password + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + describe("connect", () => { + test("should throw if missing role", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typePost} @node { + id: String + content: String + } + + type ${typeUser} @node { + id: ID + name: String + password: String + posts: [${typePost}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${typeUser} + @authorization(validate: [{ + when: [BEFORE], + operations: [CREATE_RELATIONSHIP], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + + extend type ${typePost} @authorization(validate: [{ + when: [BEFORE], + operations: [CREATE_RELATIONSHIP], + where: { jwt: { roles_INCLUDES: "super-admin" } } + }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${typeUser.plural} { + id + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${typeUser} {id: "${userId}"}) + CREATE (:${typePost} {id: "${postId}"}) + `); + // missing super-admin + const token = createBearerToken(secret, { roles: ["admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + describe("disconnect", () => { + test("should throw if missing role", async () => { + const typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typePost} @node { + id: String + content: String + } + + type ${typeUser} @node { + id: ID + name: String + password: String + posts: [${typePost}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${typeUser} + @authorization(validate: [{ + when: [BEFORE], + operations: [DELETE_RELATIONSHIP], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + + extend type ${typePost} @authorization(validate: [{ + when: [BEFORE], + operations: [DELETE_RELATIONSHIP], + where: { jwt: { roles_INCLUDES: "super-admin" } } + }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${typeUser.plural} { + id + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${typeUser} {id: "${userId}"}) + CREATE (:${typePost} {id: "${userId}"}) + `); + // missing super-admin + const token = createBearerToken(secret, { roles: ["admin"] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + describe("delete", () => { + test("should throw if missing role on type definition", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node @authorization(validate: [{ + when: [BEFORE], + operations: [DELETE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) { + id: ID + name: String + } + `; + + const query = ` + mutation { + ${typeUser.operations.delete} { + nodesDeleted + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret, { roles: [] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + + test("should throw if missing role on type definition (with nested delete)", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + name: String + posts: [${typePost}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${typePost} @node @authorization(validate: [{ + when: [BEFORE], + operations: [DELETE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) { + id: ID + name: String + } + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${typeUser.operations.delete}(where: {id_EQ: "${userId}"}, delete:{posts: {where:{node: { id_EQ: "${postId}"}}}}) { + nodesDeleted + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${typeUser} {id: "${userId}"})-[:HAS_POST]->(:${typePost} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { roles: [] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + // TODO: Move these checks into JavaScript! Fun! + describe("custom-resolvers", () => { + test("should throw if missing role on custom Query with @cypher", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @mutation(operations: []) @query(read: false, aggregate: false) @node { + id: ID + name: String + } + + type Query { + ${typeUser.plural}: [${typeUser}] @cypher(statement: "MATCH (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + const query = ` + query { + ${typeUser.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret, { roles: [] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if missing role on custom Mutation with @cypher", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeUser} @node { + id: ID + name: String + } + + type Mutation { + ${typeUser.operations.create}: ${typeUser} @cypher(statement: "CREATE (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + } + `; + + const query = ` + mutation { + ${typeUser.operations.create} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret, { roles: [] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + }); + + test("should throw if missing role on Field definition @cypher", async () => { + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! + } + + type ${typeHistory} @node { + url: String + } + + type ${typeUser} @node { + id: ID + history: [${typeHistory}] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${typeHistory}) RETURN h AS h", columnName: "h") + @authorization(validate: [{ + when: [BEFORE], + where: { jwt: { roles_INCLUDES: "admin" } } + }]) + } + `; + + const query = ` + { + ${typeUser.plural} { + history { + url + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const token = createBearerToken(secret, { roles: [] }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); + }); + }); + + describe("combining roles with where", () => { + test("combines where with roles", async () => { + const type = testHelper.createUniqueType("User"); + + const typeDefs = ` + type JWTPayload @jwt { + id: String! + roles: [String!]! + } + + type ${type.name} @node { + id: ID + name: String + password: String + } + + extend type ${type.name} + @authorization( + filter: [ + { + where: { node: { id_EQ: "$jwt.id" }, jwt: { roles_INCLUDES: "user" } } + }, + { + where: { jwt: { roles_INCLUDES: "admin" } } + } + ] + ) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const userId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + query { + ${type.plural} { + id + name + password + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${type.name} {id: "${userId}", name: "User1", password: "password" }) + CREATE (:${type.name} {id: "${userId2}", name: "User2", password: "password" }) + `); + // request with role "user" - should only return details of user + const userToken = createBearerToken(secret, { roles: ["user"], id: userId }); + + const gqlResultUser = await testHelper.executeGraphQLWithToken(query, userToken); + + expect(gqlResultUser.data).toEqual({ + [type.plural]: [{ id: userId, name: "User1", password: "password" }], + }); + + // request with role "admin" - should return all users + const adminToken = createBearerToken(secret, { roles: ["admin"], id: userId2 }); + + const gqlResultAdmin = await testHelper.executeGraphQLWithToken(query, adminToken); + + expect(gqlResultAdmin.data).toEqual({ + [type.plural]: expect.toIncludeSameMembers([ + { id: userId, name: "User1", password: "password" }, + { id: userId2, name: "User2", password: "password" }, + ]), + }); + }); + }); + + describe("rolesPath with dots", () => { + test("can read role from path containing dots", async () => { + const type = testHelper.createUniqueType("User"); + + const typeDefs = ` + type JWTPayload @jwt { + roles: [String!]! @jwtClaim(path: "https://auth0\\\\.mysite\\\\.com/claims.https://auth0\\\\.mysite\\\\.com/claims/roles") + } + + type ${type.name} @node { + id: ID + name: String + password: String + } + + extend type ${type.name} + @authorization( + validate: [ + { + when: [BEFORE], + where: { jwt: { roles_INCLUDES: "admin" } } + } + ] + ) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + query { + ${type.plural} { + id + name + password + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${type.name} {id: "${userId}", name: "User1", password: "password" }) + `); + // request without role "admin" - should return all users + const nonAdminToken = createBearerToken(secret, { + "https://auth0.mysite.com/claims": { "https://auth0.mysite.com/claims/roles": ["user"] }, + id: userId, + }); + + const gqlResultUser = await testHelper.executeGraphQLWithToken(query, nonAdminToken); + + expect((gqlResultUser.errors as any[])[0].message).toBe("Forbidden"); + + // request with role "admin" - should return all users + const adminToken = createBearerToken(secret, { + "https://auth0.mysite.com/claims": { "https://auth0.mysite.com/claims/roles": ["admin"] }, + id: userId, + }); + + const gqlResultAdmin = await testHelper.executeGraphQLWithToken(query, adminToken); + + expect(gqlResultAdmin.data).toEqual({ + [type.plural]: [{ id: userId, name: "User1", password: "password" }], + }); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-deprecated.int.test.ts new file mode 100644 index 0000000000..4761aba3d3 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-deprecated.int.test.ts @@ -0,0 +1,1456 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("type narrowing - simple case - deprecated", () => { + const testHelper = new TestHelper(); + + let Movie: UniqueType; + let AmatureProduction: UniqueType; + let Actor: UniqueType; + let UntrainedPerson: UniqueType; + + beforeEach(async () => { + Movie = testHelper.createUniqueType("Movie"); + AmatureProduction = testHelper.createUniqueType("AmatureProduction"); + Actor = testHelper.createUniqueType("Actor"); + UntrainedPerson = testHelper.createUniqueType("UntrainedPerson"); + + const typeDefs = /* GraphQL */ ` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "AppearsIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [${AmatureProduction}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("get narrowed connection field", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 63563; + const movieScreenTime = 42699; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 88310; + const seriesScreenTime = 40968; + const sceneNr = 92937; + + const query = /* GraphQL */ ` + query Productions { + productions { + title + actorsConnection { + edges { + node { + name + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["productions"]).toIncludeSameMembers([ + { + title: movieTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: amatureProductionTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field nested for one narrowed type", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 41886; + const movieScreenTime = 72079; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 34992; + const seriesScreenTime = 43724; + const sceneNr = 96768; + + const query = /* GraphQL */ ` + query Productions { + productions { + title + actorsConnection { + edges { + node { + name + ... on ${Actor} { + moviesCnt + actedInConnection { + edges { + node { + title + ... on ${Movie} { + runtime + } + } + properties { + ... on ActedIn { + screenTime + } + } + } + } + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["productions"]).toIncludeSameMembers([ + { + title: movieTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: amatureProductionTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field nested for the other narrowed type", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 12527; + const movieScreenTime = 41858; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 65818; + const seriesScreenTime = 67600; + const sceneNr = 88992; + + const query = /* GraphQL */ ` + query Productions { + productions { + title + actorsConnection { + edges { + node { + name + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + actedInConnection { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["productions"]).toIncludeSameMembers([ + { + title: movieTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + title: amatureProductionTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field + filter on edge top level", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 17297; + const movieScreenTime = 33241; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 1345; + const seriesScreenTime = 17269; + const sceneNr = 9176; + + const query = /* GraphQL */ ` + query People { + people(where: { actedInConnection_SOME: { edge: { ActedIn: { screenTime_EQ: ${movieScreenTime} }, AppearsIn: { sceneNr_EQ: ${sceneNr} } } } }) { + name + actedInConnection { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (m3:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(m3) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["people"]).toEqual( + expect.arrayContaining([ + { + name: actorName, + actedInConnection: { + edges: expect.arrayContaining([ + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.arrayContaining([ + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]) + ); + }); + + test("get narrowed connection field + filter on node top level", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 69186; + const movieScreenTime = 33869; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 88178; + const seriesScreenTime = 41685; + const sceneNr = 20583; + + const query = /* GraphQL */ ` + query People { + people(where: { actedInConnection_SOME: { node: { OR: [ { title_EQ: "${movieTitle}" }, { title_EQ: "${amatureProductionTitle}" }] } } }) { + name + actedInConnection { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["people"]).toEqual([ + { + name: actorName, + actedInConnection: { + edges: expect.arrayContaining([ + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.arrayContaining([ + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr: sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field + filter on edge nested - only one possible propertiesTypeName", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 80494; + const movieScreenTime = 34409; + const movieScreenTime2 = 12485; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 79217; + const seriesScreenTime = 60447; + const sceneNr = 85423; + + const query = /* GraphQL */ ` + query People { + people { + name + actedInConnection { + edges { + node { + title + actorsConnection(where: { edge: { ActedIn: {screenTime_EQ: ${movieScreenTime2}}, AppearsIn: {} } }) { + edges { + node { + name + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime2 }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + movieScreenTime2, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["people"]).toIncludeSameMembers([ + { + name: actorName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + actorsConnection: { + edges: [], + }, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field + filter on edge nested - other possible propertiesTypeName", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 49337; + const movieScreenTime = 80113; + const movieScreenTime2 = 5407; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 189; + const seriesScreenTime = 22550; + const sceneNr = 40154; + + const query = /* GraphQL */ ` + query People { + people { + name + actedInConnection { + edges { + node { + title + actorsConnection(where: { edge: { AppearsIn: { NOT: { sceneNr_EQ: ${sceneNr} } } } }) { + edges { + node { + name + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime2 }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + movieScreenTime2, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["people"]).toIncludeSameMembers([ + { + name: actorName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + actorsConnection: { + edges: [], + }, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("get narrowed connection field + filter on edge nested - all possible propertiesTypeName", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 12169; + const movieScreenTime = 33584; + const movieScreenTime2 = 98332; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 80053; + const seriesScreenTime = 98561; + const sceneNr = 21219; + + const query = /* GraphQL */ ` + query People { + people { + name + actedInConnection { + edges { + node { + title + actorsConnection(where: { edge: { ActedIn: { NOT: { screenTime_EQ: ${movieScreenTime} } }, AppearsIn: { NOT: { sceneNr_EQ: ${sceneNr} } } } }) { + edges { + node { + name + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime2 }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + movieScreenTime2, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.["people"]).toIncludeSameMembers([ + { + name: actorName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + actorsConnection: { + edges: [], + }, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: actorName, + moviesCnt: 1, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + actorsConnection: { + edges: [], + }, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); + + test("concrete.interfaceConnection edge filter works for the correct propertiesTypeName", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 39883; + const movieScreenTime = 64889; + const movieScreenTime2 = 82496; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 42968; + const seriesScreenTime = 64469; + const sceneNr = 2621; + + const query = /* GraphQL */ ` + query Actors { + ${Actor.plural} { + name + actedInConnection(where: { edge: { ActedIn: { screenTime_EQ: ${movieScreenTime} } } }) { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime2 }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + movieScreenTime2, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.[Actor.plural]).toIncludeSameMembers([ + { + name: actorName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + ]), + }, + }, + ]); + }); + + test("concrete.interfaceConnection edge filter ignores the incorrect propertiesTypeName (Person.actedIn can have AppearsIn properties but Actor.actedIn can only have ActedIn)", async () => { + const actorName = "actor1"; + const untrainedPersonName = "anyone"; + + const movieTitle = "movie1"; + const movieTitle2 = "movie2"; + const movieRuntime = 67705; + const movieScreenTime = 88223; + const movieScreenTime2 = 91931; + + const amatureProductionTitle = "amature"; + const seriesEpisodes = 16632; + const seriesScreenTime = 39594; + const sceneNr = 76405; + + const query = /* GraphQL */ ` + query Actors { + ${Actor.plural} { + name + actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: 0 } } }) { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime2 }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + movieScreenTime2, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data?.[Actor.plural]).toIncludeSameMembers([ + { + name: actorName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime, + }, + }, + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + screenTime: movieScreenTime2, + }, + }, + ]), + }, + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-nested-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-nested-deprecated.int.test.ts new file mode 100644 index 0000000000..da442ec54b --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/type-narrowing-nested-deprecated.int.test.ts @@ -0,0 +1,587 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { gql } from "graphql-tag"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("type narrowing nested connections - deprecated", () => { + const testHelper = new TestHelper(); + let gqlQuery: string; + + let Movie: UniqueType; + let AmatureProduction: UniqueType; + let Actor: UniqueType; + let UntrainedPerson: UniqueType; + + let actorName: string; + let untrainedPersonName: string; + let movieTitle: string; + let movieTitle2: string; + let movieRuntime: number; + let movieScreenTime: number; + let amatureProductionTitle: string; + let seriesEpisodes: number; + let seriesScreenTime: number; + let sceneNr: number; + + beforeEach(async () => { + actorName = "actor1"; + untrainedPersonName = "anyone"; + movieTitle = "movie1"; + movieTitle2 = "movie2"; + movieRuntime = 99105; + movieScreenTime = 87653; + amatureProductionTitle = "amature"; + seriesEpisodes = 607; + seriesScreenTime = 73385; + sceneNr = 16220; + + Movie = testHelper.createUniqueType("Movie"); + AmatureProduction = testHelper.createUniqueType("AmatureProduction"); + Actor = testHelper.createUniqueType("Actor"); + UntrainedPerson = testHelper.createUniqueType("UntrainedPerson"); + + await testHelper.executeCypher( + ` + CREATE (a:${Actor} { name: $actorName, moviesCnt: 1 }) + CREATE (up:${UntrainedPerson} { name: $untrainedPersonName, age: 20 }) + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (m2:${Movie} { title: $movieTitle2, runtime:$movieRuntime }) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + CREATE (a)-[:ACTED_IN { screenTime: $movieScreenTime }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr }]->(m2) + CREATE (up)-[:ACTED_IN { sceneNr: $sceneNr, screenTime: $seriesScreenTime }]->(:${AmatureProduction} { title: $amatureProductionTitle, episodeCount: $seriesEpisodes }) + `, + { + actorName, + untrainedPersonName, + movieTitle, + movieTitle2, + movieRuntime, + movieScreenTime, + seriesEpisodes, + seriesScreenTime, + amatureProductionTitle, + sceneNr, + } + ); + + gqlQuery = /* GraphQL */ ` + query Productions { + productions { + title + actorsConnection { + edges { + node { + name + actedInConnection { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + ... on ${Actor} { + moviesCnt + } + ... on ${UntrainedPerson} { + age + } + } + properties { + ... on ActedIn { + screenTime + } + + } + } + } + } + } + `; + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("connection field has relationship to one narrowed type only", async () => { + const typeDefs = gql` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [${AmatureProduction}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const gqlResult = await testHelper.executeGraphQL(gqlQuery); + + expect(gqlResult.errors).toBeFalsy(); + expect( + (gqlResult.data?.["productions"] as any).find((r) => r.title === amatureProductionTitle).actorsConnection + .edges + ).toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + properties: { + screenTime: seriesScreenTime, + }, + }, + ]); + }); + test("connection field has relationship to the other one narrowed type only", async () => { + const typeDefs = gql` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const gqlResult = await testHelper.executeGraphQL(gqlQuery); + + expect(gqlResult.errors).toBeFalsy(); + expect( + (gqlResult.data?.["productions"] as any).find((r) => r.title === amatureProductionTitle).actorsConnection + .edges + ).toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + properties: { + screenTime: seriesScreenTime, + }, + }, + ]); + }); + test("connection field has relationship to interface directly", async () => { + const typeDefs = gql` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const gqlResult = await testHelper.executeGraphQL(gqlQuery); + + expect(gqlResult.errors).toBeFalsy(); + expect( + (gqlResult.data?.["productions"] as any).find((r) => r.title === amatureProductionTitle).actorsConnection + .edges + ).toIncludeSameMembers([ + { + node: { + name: untrainedPersonName, + age: 20, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + sceneNr, + }, + }, + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + properties: { + screenTime: seriesScreenTime, + }, + }, + ]); + }); + test("concrete.interfaceConnection edge filter works for the correct propertiesTypeName", async () => { + const typeDefs = gql` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const filterQuery = /* GraphQL */ ` + query UntrainedPeople { + ${UntrainedPerson.plural} { + name + actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: 0 } } }) { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(filterQuery); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data?.[UntrainedPerson.plural]).toIncludeSameMembers([ + { + name: untrainedPersonName, + actedInConnection: { + edges: [], + }, + }, + ]); + }); + test("concrete.interfaceConnection edge filter ignores the incorrect propertiesTypeName (Person.actedIn can have ActedIn properties but UntrainedPerson.actedIn can only have AppearsIn)", async () => { + const typeDefs = gql` + interface Production { + title: String! + actors: [Person!]! @declareRelationship + } + + type ${Movie} implements Production @node { + title: String! + runtime: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ${AmatureProduction} implements Production @node { + title: String! + episodeCount: Int! + actors: [${UntrainedPerson}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + + type AppearsIn @relationshipProperties { + sceneNr: Int! + } + + interface Person { + name: String! + actedIn: [Production!]! @declareRelationship + } + + type ${Actor} implements Person @node { + name: String! + moviesCnt: Int! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ${UntrainedPerson} implements Person @node { + name: String! + age: Int! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "AppearsIn") + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + }); + + const filterQuery = /* GraphQL */ ` + query UntrainedPeople { + ${UntrainedPerson.plural} { + name + actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: ${sceneNr} }, ActedIn: {screenTime_EQ: ${movieScreenTime}} } }) { + edges { + node { + title + ... on ${Movie} { + runtime + } + ... on ${AmatureProduction} { + episodeCount + } + } + properties { + ... on ActedIn { + screenTime + } + ... on AppearsIn { + sceneNr + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(filterQuery); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data?.[UntrainedPerson.plural]).toIncludeSameMembers([ + { + name: untrainedPersonName, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: amatureProductionTitle, + episodeCount: seriesEpisodes, + }, + properties: { + sceneNr, + }, + }, + { + node: { + title: movieTitle2, + runtime: movieRuntime, + }, + properties: { + sceneNr, + }, + }, + ]), + }, + }, + ]); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/points-array-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/points-array-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..037352af5a --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/points-array-filter-deprecated.int.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("[Point] - deprecated filter - deprecated", () => { + const testHelper = new TestHelper(); + let Route: UniqueType; + + beforeEach(async () => { + Route = testHelper.createUniqueType("Route"); + const typeDefs = /* GraphQL */ ` + type ${Route} @node { + id: String! + waypoints: [Point!]! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("enables query of a node with multiple wgs-84 points", async () => { + // Create test data and prepare for testing + const id = "25c4676e-1e38-4b1b-b156-6a7e28c8013e"; + const waypoints = [...new Array(9)].map(() => ({ + longitude: parseFloat("34.1879"), + latitude: parseFloat("30.5402"), + })); + + await testHelper.executeCypher( + ` + CALL { + CREATE (r:${Route}) + SET r.id = $id + SET r.waypoints = [p in $waypoints | point(p)] + RETURN r + } + + RETURN r { .id, .waypoints } AS r + `, + { id, waypoints } + ); + + // Test for equality + const routesQuery = /* GraphQL */ ` + query Routes($waypoints: [PointInput!]) { + ${Route.plural}(where: { waypoints_EQ: $waypoints }) { + id + waypoints { + latitude + longitude + height + crs + } + } + } + `; + + const routesResult = await testHelper.executeGraphQL(routesQuery, { variableValues: { waypoints } }); + + expect(routesResult.errors).toBeFalsy(); + expect((routesResult.data as any)[Route.plural][0]).toEqual({ + id, + waypoints: waypoints.map((waypoint) => ({ ...waypoint, height: null, crs: "wgs-84" })), + }); + + // Test INCLUDES functionality + const routesIncludesQuery = /* GraphQL */ ` + query RoutesIncludes($waypoint: PointInput) { + ${Route.plural}(where: { waypoints_INCLUDES: $waypoint }) { + id + waypoints { + latitude + longitude + height + crs + } + } + } + `; + + const routesIncludesResult = await testHelper.executeGraphQL(routesIncludesQuery, { + variableValues: { waypoint: waypoints[0] }, + }); + + expect(routesIncludesResult.errors).toBeFalsy(); + expect((routesIncludesResult.data as any)[Route.plural][0]).toEqual({ + id, + waypoints: waypoints.map((waypoint) => ({ ...waypoint, height: null, crs: "wgs-84" })), + }); + }); + + test("enables query of a node with multiple wgs-84-3d points", async () => { + const id = "dd320626-cc23-4938-9f33-ba624a3a3e8d"; + const waypoints = [...new Array(7)].map(() => ({ + longitude: parseFloat("146.1568"), + latitude: parseFloat("-54.6132"), + height: 0.03157347836531699, + })); + + await testHelper.executeCypher( + ` + CALL { + CREATE (r:${Route}) + SET r.id = $id + SET r.waypoints = [p in $waypoints | point(p)] + RETURN r + } + + RETURN r { .id, .waypoints } AS r + `, + { id, waypoints } + ); + + const routesQuery = /* GraphQL */ ` + query Routes($id: String!) { + ${Route.plural}(where: { id_EQ: $id }) { + id + waypoints { + latitude + longitude + height + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(routesQuery, { + variableValues: { id }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Route.plural][0]).toEqual({ + id, + waypoints: waypoints.map((waypoint) => ({ ...waypoint, crs: "wgs-84-3d" })), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-filters-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-filters-deprecated.int.test.ts new file mode 100644 index 0000000000..724e703ac2 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-filters-deprecated.int.test.ts @@ -0,0 +1,362 @@ +import neo4jDriver from "neo4j-driver"; +import { generate } from "randomstring"; +import { parseDuration } from "../../../../../src/graphql/scalars/Duration"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("types filtering - deprecated", () => { + const testHelper = new TestHelper(); + let Movie: UniqueType; + let File: UniqueType; + + beforeEach(() => { + Movie = testHelper.createUniqueType("Movie"); + File = testHelper.createUniqueType("File"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should find a movie (with a Date) - deprecated", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie.name} @node { + date: Date + } + `; + + const date = new Date(); + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}(where: { date_EQ: "${date.toISOString()}" }) { + date + } + } + `; + + const nDate = neo4jDriver.types.Date.fromStandardDate(date); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie.name}) + SET m.date = $nDate + `, + { nDate } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Movie.plural][0]).toEqual({ + date: date.toISOString().split("T")[0], + }); + }); + + test("should find a movie (with a DateTime) - deprecated", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie.name} @node { + datetime: DateTime + } + `; + + const date = new Date(); + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const query = ` + query { + ${Movie.plural}(where: { datetime_EQ: "${date.toISOString()}" }) { + datetime + } + } + `; + + const nDateTime = neo4jDriver.types.DateTime.fromStandardDate(date); + + await testHelper.executeCypher( + ` + CREATE (m:${Movie.name}) + SET m.datetime = $nDateTime + `, + { nDateTime } + ); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Movie.plural][0]).toEqual({ datetime: date.toISOString() }); + }); + + test("should find a movie (with a DateTime created with a timezone) - deprecated", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie.name} @node { + name: String + datetime: DateTime + } + `; + + const date = new Date(); + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}(where: { name_EQ: "${Movie.name}" }) { + datetime + } + } + `; + + await testHelper.executeCypher(` + CREATE (m:${Movie.name}) + SET m.name = "${Movie.name}" + SET m.datetime = datetime("${date.toISOString().replace("Z", "[Etc/UTC]")}") + `); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Movie.plural][0]).toEqual({ datetime: date.toISOString() }); + }); + + test("should filter based on duration equality - deprecated", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + duration: Duration! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ readable: false }); + const days = 4; + const duration = `P${days}D`; + const parsedDuration = parseDuration(duration); + const neo4jDuration = new neo4jDriver.types.Duration(0, days, 0, 0); + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie}) + SET movie = $movie + `, + { movie: { id, duration: neo4jDuration } } + ); + + const query = /* GraphQL */ ` + query ($id: ID!, $duration: Duration!) { + ${Movie.plural}(where: { id_EQ: $id, duration_EQ: $duration }) { + id + duration + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, duration }, + }); + + expect(graphqlResult.errors).toBeFalsy(); + + const graphqlMovie: { id: string; duration: string } = (graphqlResult.data as any)[Movie.plural][0]; + expect(graphqlMovie).toBeDefined(); + expect(graphqlMovie.id).toEqual(id); + expect(parseDuration(graphqlMovie.duration)).toStrictEqual(parsedDuration); + }); + + test.each(["LT", "LTE", "GT", "GTE"])( + "should filter based on duration comparison, for filter: %s - deprecated", + async (filter) => { + const typeDefs = ` + type ${Movie} @node { + id: ID! + duration: Duration! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const longId = generate({ readable: false }); + const long = "P2Y"; + const parsedLong = parseDuration(long); + const neo4jLong = new neo4jDriver.types.Duration( + parsedLong.months, + parsedLong.days, + parsedLong.seconds, + parsedLong.nanoseconds + ); + + const mediumId = generate({ readable: false }); + const medium = "P2M"; + const parsedMedium = parseDuration(medium); + const neo4jMedium = new neo4jDriver.types.Duration( + parsedMedium.months, + parsedMedium.days, + parsedMedium.seconds, + parsedMedium.nanoseconds + ); + + const shortId = generate({ readable: false }); + const short = "P2D"; + const parsedShort = parseDuration(short); + const neo4jShort = new neo4jDriver.types.Duration( + parsedShort.months, + parsedShort.days, + parsedShort.seconds, + parsedShort.nanoseconds + ); + + await testHelper.executeCypher( + ` + CREATE (long:${Movie}) + SET long = $long + CREATE (medium:${Movie}) + SET medium = $medium + CREATE (short:${Movie}) + SET short = $short + `, + { + long: { id: longId, duration: neo4jLong }, + medium: { id: mediumId, duration: neo4jMedium }, + short: { id: shortId, duration: neo4jShort }, + } + ); + + const query = /* GraphQL */ ` + query ($where: ${Movie.name}Where!) { + ${Movie.plural}(where: $where, sort: [{ duration: ASC }]) { + id + duration + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { + where: { id_IN: [longId, mediumId, shortId], [`duration_${filter}`]: medium }, + }, + }); + + expect(graphqlResult.errors).toBeUndefined(); + + const graphqlMovies: { id: string; duration: string }[] = (graphqlResult.data as any)[Movie.plural]; + expect(graphqlMovies).toBeDefined(); + + /* eslint-disable jest/no-conditional-expect */ + if (filter === "LT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(shortId); + expect(parseDuration(graphqlMovies[0]?.duration as string)).toStrictEqual(parsedShort); + } + + if (filter === "LTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(shortId); + expect(parseDuration(graphqlMovies[0]?.duration as string)).toStrictEqual(parsedShort); + + expect(graphqlMovies[1]?.id).toBe(mediumId); + expect(parseDuration(graphqlMovies[1]?.duration as string)).toStrictEqual(parsedMedium); + } + + if (filter === "GT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(longId); + expect(parseDuration(graphqlMovies[0]?.duration as string)).toStrictEqual(parsedLong); + } + + if (filter === "GTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(mediumId); + expect(parseDuration(graphqlMovies[0]?.duration as string)).toStrictEqual(parsedMedium); + + expect(graphqlMovies[1]?.id).toBe(longId); + expect(parseDuration(graphqlMovies[1]?.duration as string)).toStrictEqual(parsedLong); + } + /* eslint-enable jest/no-conditional-expect */ + } + ); + + test("BigInt should work returning a BigInt property - deprecated", async () => { + const name = generate({ + charset: "alphabetic", + }); + + const typeDefs = ` + type ${File} @node { + name: String! + size: BigInt! @cypher(statement: """ + RETURN 9223372036854775807 as result + """, columnName:"result") + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const query = ` + query { + ${File.plural}(where: { name_EQ: "${name}" }) { + name + size + } + } + `; + + await testHelper.executeCypher(` + CREATE (f:${File}) + SET f.name = "${name}" + `); + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult?.data).toEqual({ + [File.plural]: [ + { + name, + size: "9223372036854775807", + }, + ], + }); + }); + + test("float should return normal JS number if the value isInt - deprecated filter", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: String + fakeFloat: Float! @cypher(statement: """ + RETURN 12345 as result + """, columnName: "result") + } + + + `; + + const id = generate({ + charset: "alphabetic", + }); + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const query = /* GraphQL */ ` + query { + ${Movie.plural}(where: { id_EQ: "${id}" }){ + fakeFloat + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { id: "${id}" }) + `, + {} + ); + const gqlResult = await testHelper.executeGraphQL(query, {}); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Movie.plural][0].fakeFloat).toBe(12345); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localdatetime-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localdatetime-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..df29dbbcab --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localdatetime-filter-deprecated.int.test.ts @@ -0,0 +1,194 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import neo4jDriver from "neo4j-driver"; +import { generate } from "randomstring"; +import { parseLocalDateTime } from "../../../../../src/graphql/scalars/LocalDateTime"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("LocalDateTime - deprecated filters", () => { + const testHelper = new TestHelper(); + let Movie: UniqueType; + + beforeEach(async () => { + Movie = testHelper.createUniqueType("Movie"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + localDT: LocalDateTime + localDTs: [LocalDateTime!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should filter based on localDT equality", async () => { + const id = generate({ readable: false }); + const date = new Date("2024-09-17T11:49:48.322Z"); + const localDT = date.toISOString().split("Z")[0]; + const neo4jLocalDateTime = neo4jDriver.types.LocalDateTime.fromStandardDate(date); + const parsedLocalDateTime = parseLocalDateTime(localDT); + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie}) + SET movie = $movie + `, + { movie: { id, localDT: neo4jLocalDateTime } } + ); + + const query = /* GraphQL */ ` + query ($localDT: LocalDateTime!) { + ${Movie.plural}(where: { localDT_EQ: $localDT }) { + id + localDT + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { localDT }, + }); + + expect(graphqlResult.errors).toBeFalsy(); + + const graphqlMovie: { id: string; localDT: string } = (graphqlResult.data as any)[Movie.plural][0]; + expect(graphqlMovie).toBeDefined(); + expect(graphqlMovie.id).toEqual(id); + expect(parseLocalDateTime(graphqlMovie.localDT)).toStrictEqual(parsedLocalDateTime); + }); + test.each(["LT", "LTE", "GT", "GTE"])( + "should filter based on localDT comparison, for filter %s", + async (filter) => { + const futureId = generate({ readable: false }); + const future = "6025-02-18T18:10:55.462Z".split("Z")[0]; + const parsedFuture = parseLocalDateTime(future); + const neo4jFuture = new neo4jDriver.types.LocalDateTime( + parsedFuture.year, + parsedFuture.month, + parsedFuture.day, + parsedFuture.hour, + parsedFuture.minute, + parsedFuture.second, + parsedFuture.nanosecond + ); + + const presentId = generate({ readable: false }); + const present = new Date().toISOString().split("Z")[0]; + const parsedPresent = parseLocalDateTime(present); + const neo4jPresent = new neo4jDriver.types.LocalDateTime( + parsedPresent.year, + parsedPresent.month, + parsedPresent.day, + parsedPresent.hour, + parsedPresent.minute, + parsedPresent.second, + parsedPresent.nanosecond + ); + + const pastId = generate({ readable: false }); + const past = "2022-08-29T10:21:43.108Z".split("Z")[0]; + const parsedPast = parseLocalDateTime(past); + const neo4jPast = new neo4jDriver.types.LocalDateTime( + parsedPast.year, + parsedPast.month, + parsedPast.day, + parsedPast.hour, + parsedPast.minute, + parsedPast.second, + parsedPast.nanosecond + ); + + await testHelper.executeCypher( + ` + CREATE (future:${Movie}) + SET future = $future + CREATE (present:${Movie}) + SET present = $present + CREATE (past:${Movie}) + SET past = $past + `, + { + future: { id: futureId, localDT: neo4jFuture }, + present: { id: presentId, localDT: neo4jPresent }, + past: { id: pastId, localDT: neo4jPast }, + } + ); + + const query = /* GraphQL */ ` + query ($where: ${Movie.name}Where!) { + ${Movie.plural}(where: $where, sort: [{ localDT: ASC }]) { + id + localDT + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { + where: { id_IN: [futureId, presentId, pastId], [`localDT_${filter}`]: present }, + }, + }); + + expect(graphqlResult.errors).toBeUndefined(); + + const graphqlMovies: { id: string; localDT: string }[] = (graphqlResult.data as any)[Movie.plural]; + expect(graphqlMovies).toBeDefined(); + + /* eslint-disable jest/no-conditional-expect */ + if (filter === "LT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPast); + } + + if (filter === "LTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPast); + + expect(graphqlMovies[1]?.id).toBe(presentId); + expect(parseLocalDateTime(graphqlMovies[1]?.localDT)).toStrictEqual(parsedPresent); + } + + if (filter === "GT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(futureId); + expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedFuture); + } + + if (filter === "GTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(presentId); + expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPresent); + + expect(graphqlMovies[1]?.id).toBe(futureId); + expect(parseLocalDateTime(graphqlMovies[1]?.localDT)).toStrictEqual(parsedFuture); + } + /* eslint-enable jest/no-conditional-expect */ + } + ); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localtime-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localtime-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..30d6bb1011 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-localtime-filter-deprecated.int.test.ts @@ -0,0 +1,182 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import neo4jDriver from "neo4j-driver"; +import { generate } from "randomstring"; +import { parseLocalTime } from "../../../../../src/graphql/scalars/LocalTime"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("LocalTime - deprecated filters", () => { + const testHelper = new TestHelper(); + let Movie: UniqueType; + + beforeEach(async () => { + Movie = testHelper.createUniqueType("Movie"); + + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + time: LocalTime + times: [LocalTime!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should filter based on time equality", async () => { + const id = generate({ readable: false }); + const date = new Date("2024-09-17T11:49:48.322Z"); + const time = date.toISOString().split("T")[1]?.split("Z")[0]; + const neo4jTime = neo4jDriver.types.LocalTime.fromStandardDate(date); + const parsedTime = parseLocalTime(time); + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie}) + SET movie = $movie + `, + { movie: { id, time: neo4jTime } } + ); + + const query = /* GraphQL */ ` + query ($time: LocalTime!) { + ${Movie.plural}(where: { time_EQ: $time }) { + id + time + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, time }, + }); + + expect(graphqlResult.errors).toBeFalsy(); + + const graphqlMovie: { id: string; time: string } = (graphqlResult.data as any)[Movie.plural][0]; + expect(graphqlMovie).toBeDefined(); + expect(graphqlMovie.id).toEqual(id); + expect(parseLocalTime(graphqlMovie.time)).toStrictEqual(parsedTime); + }); + test.each(["LT", "LTE", "GT", "GTE"])("should filter based on time comparison, for filter %s", async (filter) => { + const futureId = generate({ readable: false }); + const future = "13:00:00"; + const parsedFuture = parseLocalTime(future); + const neo4jFuture = new neo4jDriver.types.LocalTime( + parsedFuture.hour, + parsedFuture.minute, + parsedFuture.second, + parsedFuture.nanosecond + ); + + const presentId = generate({ readable: false }); + const present = "12:00:00"; + const parsedPresent = parseLocalTime(present); + const neo4jPresent = new neo4jDriver.types.LocalTime( + parsedPresent.hour, + parsedPresent.minute, + parsedPresent.second, + parsedPresent.nanosecond + ); + + const pastId = generate({ readable: false }); + const past = "11:00:00"; + const parsedPast = parseLocalTime(past); + const neo4jPast = new neo4jDriver.types.LocalTime( + parsedPast.hour, + parsedPast.minute, + parsedPast.second, + parsedPast.nanosecond + ); + + await testHelper.executeCypher( + ` + CREATE (future:${Movie}) + SET future = $future + CREATE (present:${Movie}) + SET present = $present + CREATE (past:${Movie}) + SET past = $past + `, + { + future: { id: futureId, time: neo4jFuture }, + present: { id: presentId, time: neo4jPresent }, + past: { id: pastId, time: neo4jPast }, + } + ); + + const query = /* GraphQL */ ` + query ($where: ${Movie.name}Where!) { + ${Movie.plural}(where: $where, sort: [{ time: ASC }]) { + id + time + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { + where: { id_IN: [futureId, presentId, pastId], [`time_${filter}`]: present }, + }, + }); + + expect(graphqlResult.errors).toBeUndefined(); + + const graphqlMovies: { id: string; time: string }[] = (graphqlResult.data as any)[Movie.plural]; + expect(graphqlMovies).toBeDefined(); + + /* eslint-disable jest/no-conditional-expect */ + if (filter === "LT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + } + + if (filter === "LTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + + expect(graphqlMovies[1]?.id).toBe(presentId); + expect(parseLocalTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + } + + if (filter === "GT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(futureId); + expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + } + + if (filter === "GTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(presentId); + expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPresent); + + expect(graphqlMovies[1]?.id).toBe(futureId); + expect(parseLocalTime(graphqlMovies[1]?.time)).toStrictEqual(parsedFuture); + } + /* eslint-enable jest/no-conditional-expect */ + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-cartesian-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-cartesian-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..9dba0f1d8b --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-cartesian-filter-deprecated.int.test.ts @@ -0,0 +1,268 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { int } from "neo4j-driver"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("CartesianPoint", () => { + const testHelper = new TestHelper(); + + let Part: UniqueType; + + beforeEach(async () => { + Part = testHelper.createUniqueType("Part"); + const typeDefs = /* GraphQL */ ` + type ${Part} @node { + serial: String! + location: CartesianPoint! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("enables update of a node with a cartesian point", async () => { + const serial = "f5e40cae-f839-4ec3-a12b-45da40e8de7f"; + const x = 0.9797746981494129; + const y = 0.6287962510250509; + const newY = 0.8291290057823062; + + const beforeResult = await testHelper.executeCypher(` + CALL { + CREATE (p:${Part}) + SET p.serial = "${serial}" + SET p.location = point({x: ${x}, y: ${y}}) + RETURN p + } + + RETURN p { .serial, .location } AS p + `); + + expect((beforeResult.records[0] as any).toObject().p.location.x).toEqual(x); + expect((beforeResult.records[0] as any).toObject().p.location.y).toEqual(y); + + const update = /* GraphQL */ ` + mutation UpdateParts($serial: String!, $x: Float!, $y: Float!) { + ${Part.operations.update}(where: { serial_EQ: $serial }, update: { location_SET: { x: $x, y: $y } }) { + ${Part.plural} { + serial + location { + x + y + z + crs + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(update, { + variableValues: { serial, x, y: newY }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.operations.update][Part.plural][0]).toEqual({ + serial, + location: { + x, + y: newY, + z: null, + crs: "cartesian", + }, + }); + + const result = await testHelper.executeCypher(` + MATCH (p:${Part} {serial: "${serial}"}) + RETURN p { .serial, .location} as p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(x); + expect((result.records[0] as any).toObject().p.location.y).toEqual(newY); + expect((result.records[0] as any).toObject().p.location.srid).toEqual(int(7203)); + }); + + test("enables update of a node with a cartesian-3d point", async () => { + const serial = "ff4af64d-90f6-4f74-ad65-13dce16502bc"; + const x = 0.954262402607128; + const y = 0.9950293721631169; + const z = 0.30888770753517747; + const newY = 0.8628286835737526; + + const beforeResult = await testHelper.executeCypher(` + CALL { + CREATE (p:${Part}) + SET p.serial = "${serial}" + SET p.location = point({x: ${x}, y: ${y}, z: ${z}}) + RETURN p + } + + RETURN p { .serial, .location } AS p + `); + + expect((beforeResult.records[0] as any).toObject().p.location.x).toEqual(x); + expect((beforeResult.records[0] as any).toObject().p.location.y).toEqual(y); + expect((beforeResult.records[0] as any).toObject().p.location.z).toEqual(z); + + const update = /* GraphQL */ ` + mutation UpdateParts($serial: String!, $x: Float!, $y: Float!, $z: Float!) { + ${Part.operations.update}(where: { serial_EQ: $serial }, update: { location_SET: { x: $x, y: $y, z: $z } }) { + ${Part.plural} { + serial + location { + x + y + z + crs + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(update, { + variableValues: { serial, x, y: newY, z }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.operations.update][Part.plural][0]).toEqual({ + serial, + location: { + x, + y: newY, + z, + crs: "cartesian-3d", + }, + }); + + const result = await testHelper.executeCypher(` + MATCH (p:${Part} {serial: "${serial}"}) + RETURN p { .serial, .location} as p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(x); + expect((result.records[0] as any).toObject().p.location.y).toEqual(newY); + expect((result.records[0] as any).toObject().p.location.z).toEqual(z); + expect((result.records[0] as any).toObject().p.location.srid).toEqual(int(9157)); + }); + + test("enables query of a node with a cartesian point", async () => { + const serial = "bf92efbc-6c15-40ac-9cd5-8ab95fa4da90"; + const x = 0.2800768264569342; + const y = 0.6105434170458466; + + const result = await testHelper.executeCypher(` + CALL { + CREATE (p:${Part}) + SET p.serial = "${serial}" + SET p.location = point({x: ${x}, y: ${y}}) + RETURN p + } + + RETURN p { .id, .location } AS p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(x); + expect((result.records[0] as any).toObject().p.location.y).toEqual(y); + + const partsQuery = /* GraphQL */ ` + query Parts($serial: String!) { + ${Part.plural}(where: { serial_EQ: $serial }) { + serial + location { + x + y + z + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(partsQuery, { + variableValues: { serial }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.plural][0]).toEqual({ + serial, + location: { + x, + y, + z: null, + crs: "cartesian", + }, + }); + }); + + test("enables query of a node with a cartesian-3d point", async () => { + const serial = "c7715adc-9c9c-45ff-b4ed-6cb20bb044ad"; + const x = 0.9446345162577927; + const y = 0.7858678111806512; + const z = 0.4248296618461609; + + const result = await testHelper.executeCypher(` + CALL { + CREATE (p:${Part}) + SET p.serial = "${serial}" + SET p.location = point({x: ${x}, y: ${y}, z: ${z}}) + RETURN p + } + + RETURN p { .id, .location } AS p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(x); + expect((result.records[0] as any).toObject().p.location.y).toEqual(y); + expect((result.records[0] as any).toObject().p.location.z).toEqual(z); + + const partsQuery = /* GraphQL */ ` + query Parts($serial: String!) { + ${Part.plural}(where: { serial_EQ: $serial }) { + serial + location { + x + y + z + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(partsQuery, { + variableValues: { serial }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.plural][0]).toEqual({ + serial, + location: { + x, + y, + z, + crs: "cartesian-3d", + }, + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..c4e1330ecb --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-point-filter-deprecated.int.test.ts @@ -0,0 +1,411 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { int } from "neo4j-driver"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Point - deprecated", () => { + const testHelper = new TestHelper(); + + let Photograph: UniqueType; + + beforeEach(async () => { + Photograph = testHelper.createUniqueType("Photograph"); + + const typeDefs = /* GraphQL */ ` + type ${Photograph} @node { + id: String! + size: Int! + location: Point! + } + + type Query { + custom: String! + } + `; + // Dummy custom resolvers to validate fix for https://github.com/neo4j/graphql/issues/278 + const resolvers = { + Query: { + custom: () => "hello", + }, + }; + await testHelper.initNeo4jGraphQL({ typeDefs, resolvers }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("enables update of a node with a wgs-84 point", async () => { + const id = "09132bb0-504a-407c-9096-6d945695dc89"; + const size = 95026; + const longitude = parseFloat("117.5113"); + const latitude = parseFloat("9.5509"); + const newLatitude = parseFloat("10.6116"); + + const beforeResult = await testHelper.executeCypher(` + CALL { + CREATE (p:${Photograph}) + SET p.id = "${id}" + SET p.size = ${size} + SET p.location = point({longitude: ${longitude}, latitude: ${latitude}}) + RETURN p + } + + RETURN p { .id, .size, .location } AS p + `); + + expect((beforeResult.records[0] as any).toObject().p.location.x).toEqual(longitude); + expect((beforeResult.records[0] as any).toObject().p.location.y).toEqual(latitude); + + const update = /* GraphQL */ ` + mutation UpdatePhotographs($id: String!, $longitude: Float!, $latitude: Float!) { + ${Photograph.operations.update}( + where: { id_EQ: $id } + update: { location_SET: { longitude: $longitude, latitude: $latitude } } + ) { + ${Photograph.plural} { + id + size + location { + latitude + longitude + height + crs + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(update, { + variableValues: { id, longitude, latitude: newLatitude }, + }); + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Photograph.operations.update][Photograph.plural][0]).toEqual({ + id, + size, + location: { + latitude: newLatitude, + longitude, + height: null, + crs: "wgs-84", + }, + }); + + const result = await testHelper.executeCypher(` + MATCH (p:${Photograph} {id: "${id}"}) + RETURN p { .id, .size, .location} as p + `); + + expect((result.records[0] as any)?.toObject().p.location.x).toEqual(longitude); + expect((result.records[0] as any)?.toObject().p.location.y).toEqual(newLatitude); + expect((result.records[0] as any)?.toObject().p.location.srid).toEqual(int(4326)); + }); + + test("enables update of a node with a wgs-84-3d point", async () => { + const id = "3b3170d8-03ed-43be-ae02-876a4233e2c7"; + const size = 55312; + const longitude = parseFloat("102.1785"); + const latitude = parseFloat("78.8688"); + const height = 0.9209601751063019; + const newLatitude = parseFloat("71.2271"); + + const beforeResult = await testHelper.executeCypher(` + CALL { + CREATE (p:${Photograph}) + SET p.id = "${id}" + SET p.size = ${size} + SET p.location = point({longitude: ${longitude}, latitude: ${latitude}, height: ${height}}) + RETURN p + } + + RETURN p { .id, .size, .location } AS p + `); + + expect((beforeResult.records[0] as any).toObject().p.location.x).toEqual(longitude); + expect((beforeResult.records[0] as any).toObject().p.location.y).toEqual(latitude); + expect((beforeResult.records[0] as any).toObject().p.location.z).toEqual(height); + + const update = /* GraphQL */ ` + mutation UpdatePhotographs($id: String!, $longitude: Float!, $latitude: Float!, $height: Float!) { + ${Photograph.operations.update}( + where: { id_EQ: $id } + update: { location_SET: { longitude: $longitude, latitude: $latitude, height: $height } } + ) { + ${Photograph.plural} { + id + size + location { + latitude + longitude + height + crs + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(update, { + variableValues: { id, longitude, latitude: newLatitude, height }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Photograph.operations.update][Photograph.plural][0]).toEqual({ + id, + size, + location: { + latitude: newLatitude, + longitude, + height, + crs: "wgs-84-3d", + }, + }); + + const result = await testHelper.executeCypher(` + MATCH (p:${Photograph} {id: "${id}"}) + RETURN p { .id, .size, .location} as p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(longitude); + expect((result.records[0] as any).toObject().p.location.y).toEqual(newLatitude); + expect((result.records[0] as any).toObject().p.location.z).toEqual(height); + expect((result.records[0] as any).toObject().p.location.srid).toEqual(int(4979)); + }); + + test("enables query of a node with a wgs-84 point", async () => { + // Create node + const id = "f8a5a58a-7380-4a39-9103-07a2c0528d8e"; + const size = 31364; + const longitude = parseFloat("62.5196"); + const latitude = parseFloat("-41.1021"); + + const result = await testHelper.executeCypher(` + CALL { + CREATE (p:${Photograph}) + SET p.id = "${id}" + SET p.size = ${size} + SET p.location = point({longitude: ${longitude}, latitude: ${latitude}}) + RETURN p + } + + RETURN p { .id, .size, .location } AS p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(longitude); + expect((result.records[0] as any).toObject().p.location.y).toEqual(latitude); + + // Test equality + const photographsEqualsQuery = /* GraphQL */ ` + query Photographs($longitude: Float!, $latitude: Float!) { + ${Photograph.plural}(where: { location_EQ: { longitude: $longitude, latitude: $latitude } }) { + id + size + location { + latitude + longitude + height + crs + } + } + } + `; + + const equalsResult = await testHelper.executeGraphQL(photographsEqualsQuery, { + variableValues: { longitude, latitude }, + }); + + expect(equalsResult.errors).toBeFalsy(); + expect((equalsResult.data as any)[Photograph.plural][0]).toEqual({ + id, + size, + location: { + latitude, + longitude, + height: null, + crs: "wgs-84", + }, + }); + + // Test IN functionality + const photographsInQuery = /* GraphQL */ ` + query Photographs($locations: [PointInput!]) { + ${Photograph.plural}(where: { location_IN: $locations }) { + id + size + location { + latitude + longitude + height + crs + } + } + } + `; + + const inResult = await testHelper.executeGraphQL(photographsInQuery, { + variableValues: { + locations: [ + { longitude, latitude }, + { + longitude: parseFloat("-156.8208"), + latitude: parseFloat("64.9108"), + }, + ], + }, + }); + + expect(inResult.errors).toBeFalsy(); + expect((inResult.data as any)[Photograph.plural]).toContainEqual({ + id, + size, + location: { + latitude, + longitude, + height: null, + crs: "wgs-84", + }, + }); + + // Test less than + const photographsLessThanQuery = /* GraphQL */ ` + query Photographs($longitude: Float!, $latitude: Float!) { + ${Photograph.plural}( + where: { location_LT: { point: { longitude: $longitude, latitude: $latitude }, distance: 1000000 } } + ) { + id + size + location { + latitude + longitude + height + crs + } + } + } + `; + + const lessThanResult = await testHelper.executeGraphQL(photographsLessThanQuery, { + variableValues: { longitude, latitude: latitude + 1 }, + }); + + expect(lessThanResult.errors).toBeFalsy(); + expect((lessThanResult.data as any)[Photograph.plural]).toContainEqual({ + id, + size, + location: { + latitude, + longitude, + height: null, + crs: "wgs-84", + }, + }); + + // Test greater than + const photographsGreaterThanQuery = /* GraphQL */ ` + query Photographs($longitude: Float!, $latitude: Float!) { + ${Photograph.plural}( + where: { location_GT: { point: { longitude: $longitude, latitude: $latitude }, distance: 1 } } + ) { + id + size + location { + latitude + longitude + height + crs + } + } + } + `; + + const greaterThanResult = await testHelper.executeGraphQL(photographsGreaterThanQuery, { + variableValues: { longitude, latitude: latitude + 1 }, + }); + + expect(greaterThanResult.errors).toBeFalsy(); + expect((greaterThanResult.data as any)[Photograph.plural]).toContainEqual({ + id, + size, + location: { + latitude, + longitude, + height: null, + crs: "wgs-84", + }, + }); + }); + + test("enables query for equality of a node with a wgs-84-3d point", async () => { + const id = "3019fe82-5231-4103-8662-39c1fcc7d50c"; + const size = 99119; + const longitude = parseFloat("125.6358"); + const latitude = parseFloat("-7.2045"); + const height = 0.6950517320074141; + + const result = await testHelper.executeCypher(` + CALL { + CREATE (p:${Photograph}) + SET p.id = "${id}" + SET p.size = ${size} + SET p.location = point({longitude: ${longitude}, latitude: ${latitude}, height: ${height}}) + RETURN p + } + + RETURN p { .id, .size, .location } AS p + `); + + expect((result.records[0] as any).toObject().p.location.x).toEqual(longitude); + expect((result.records[0] as any).toObject().p.location.y).toEqual(latitude); + expect((result.records[0] as any).toObject().p.location.z).toEqual(height); + + const photographsQuery = /* GraphQL */ ` + query Photographs($longitude: Float!, $latitude: Float!, $height: Float) { + ${Photograph.plural}(where: { location_EQ: { longitude: $longitude, latitude: $latitude, height: $height } }) { + id + size + location { + latitude + longitude + height + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(photographsQuery, { + variableValues: { longitude, latitude, height }, + }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Photograph.plural][0]).toEqual({ + id, + size, + location: { + latitude, + longitude, + height, + crs: "wgs-84-3d", + }, + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts new file mode 100644 index 0000000000..6275f1ebf8 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts @@ -0,0 +1,174 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Time - deprecated filters", () => { + const testHelper = new TestHelper(); + + let Movie: UniqueType; + + beforeEach(() => { + Movie = testHelper.createUniqueType("Movie"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should filter based on time equality", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + time: Time! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ readable: false }); + const time = "11:49:48.322000000Z"; + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie}) + SET movie.id = $id + SET movie.time = time($time) + `, + { id, time } + ); + + const query = /* GraphQL */ ` + query ($time: Time!) { + ${Movie.plural}(where: { time_EQ: $time }) { + id + time + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { variableValues: { time } }); + + expect(graphqlResult.errors).toBeFalsy(); + + const graphqlMovie: { id: string; time: string } = (graphqlResult.data as any)[Movie.plural][0]; + expect(graphqlMovie).toBeDefined(); + expect(graphqlMovie.id).toEqual(id); + expect(graphqlMovie.time).toStrictEqual(time); + }); + + test.each(["LT", "LTE", "GT", "GTE"])("should filter based on time comparison for filter: %s", async (filter) => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + time: Time! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const futureId = generate({ readable: false }); + const future = "13:00:00Z"; + + const presentId = generate({ readable: false }); + const present = "12:00:00Z"; + + const pastId = generate({ readable: false }); + const past = "11:00:00Z"; + + await testHelper.executeCypher( + ` + CREATE (future:${Movie}) + SET future.id = $futureId + SET future.time = time($future) + CREATE (present:${Movie}) + SET present.id = $presentId + SET present.time = time($present) + CREATE (past:${Movie}) + SET past.id = $pastId + SET past.time = time($past) + `, + { + futureId, + future, + presentId, + present, + pastId, + past, + } + ); + + const query = /* GraphQL */ ` + query ($where: ${Movie.name}Where!) { + ${Movie.plural}( + where: $where + sort: [{ time: ASC }] + ) { + id + time + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(query, { + variableValues: { + where: { id_IN: [futureId, presentId, pastId], [`time_${filter}`]: present }, + }, + }); + + expect(graphqlResult.errors).toBeUndefined(); + + const graphqlMovies: { id: string; time: string }[] = (graphqlResult.data as any)[Movie.plural]; + expect(graphqlMovies).toBeDefined(); + + /* eslint-disable jest/no-conditional-expect */ + if (filter === "LT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(graphqlMovies[0]?.time).toStrictEqual(past); + } + + if (filter === "LTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(pastId); + expect(graphqlMovies[0]?.time).toStrictEqual(past); + + expect(graphqlMovies[1]?.id).toBe(presentId); + expect(graphqlMovies[1]?.time).toStrictEqual(present); + } + + if (filter === "GT") { + expect(graphqlMovies).toHaveLength(1); + expect(graphqlMovies[0]?.id).toBe(futureId); + expect(graphqlMovies[0]?.time).toStrictEqual(future); + } + + if (filter === "GTE") { + expect(graphqlMovies).toHaveLength(2); + expect(graphqlMovies[0]?.id).toBe(presentId); + expect(graphqlMovies[0]?.time).toStrictEqual(present); + + expect(graphqlMovies[1]?.id).toBe(futureId); + expect(graphqlMovies[1]?.time).toStrictEqual(future); + } + /* eslint-enable jest/no-conditional-expect */ + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-update-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-update-deprecated.int.test.ts new file mode 100644 index 0000000000..071db862e4 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-update-deprecated.int.test.ts @@ -0,0 +1,149 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import neo4jDriver from "neo4j-driver"; +import { generate } from "randomstring"; +import { parseDuration } from "../../../../../src/graphql/scalars/Duration"; +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Date", () => { + const testHelper = new TestHelper(); + let Movie: UniqueType; + + beforeEach(() => { + Movie = testHelper.createUniqueType("Movie"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should update a movie (with a Date)", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID + date: Date + } + `; + + const date = new Date(); + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const create = /* GraphQL */ ` + mutation { + ${ + Movie.operations.update + }(where: { id_EQ: "${id}"}, update: { date_SET: "${date.toISOString()}" }) { + ${Movie.plural} { + id + date + } + } + } + `; + + await testHelper.executeCypher(` + CREATE (m:${Movie} {id: "${id}"}) + `); + + const gqlResult = await testHelper.executeGraphQL(create); + + expect(gqlResult.errors).toBeFalsy(); + + const result = await testHelper.executeCypher(` + MATCH (m:${Movie} {id: "${id}"}) + RETURN m {.id, .date} as m + `); + + const movie: { + id: string; + date: typeof neo4jDriver.types.Date; + } = (result.records[0] as any).toObject().m; + + expect(movie.id).toEqual(id); + expect(movie.date.toString()).toEqual(date.toISOString().split("T")[0]); + }); + + test("should update a movie (with a Duration)", async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! + duration: Duration + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ readable: false }); + const duration = "-P5Y6M"; + const parsedDuration = parseDuration(duration); + + await testHelper.executeCypher( + ` + CREATE (movie:${Movie}) + SET movie = $movie + `, + { movie: { id } } + ); + + const mutation = /* GraphQL */ ` + mutation ($id: ID!, $duration: Duration) { + ${Movie.operations.update}(where: { id_EQ: $id }, update: { duration_SET: $duration }) { + ${Movie.plural} { + id + duration + } + } + } + `; + + const graphqlResult = await testHelper.executeGraphQL(mutation, { + variableValues: { id, duration }, + }); + + expect(graphqlResult.errors).toBeFalsy(); + + const graphqlMovie: { id: string; duration: string } = (graphqlResult.data as any)[Movie.operations.update][ + Movie.plural + ][0]; + expect(graphqlMovie).toBeDefined(); + expect(graphqlMovie.id).toEqual(id); + expect(parseDuration(graphqlMovie.duration)).toStrictEqual(parsedDuration); + + const neo4jResult = await testHelper.executeCypher( + ` + MATCH (movie:${Movie} {id: $id}) + RETURN movie {.id, .duration} as movie + `, + { id } + ); + + const neo4jMovie: { id: string; duration: { toString(): string } } = neo4jResult.records[0]?.toObject().movie; + expect(neo4jMovie).toBeDefined(); + expect(neo4jMovie.id).toEqual(id); + expect(neo4jDriver.isDuration(neo4jMovie.duration)).toBe(true); + expect(parseDuration(neo4jMovie.duration.toString())).toStrictEqual(parsedDuration); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/where-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/where-deprecated.int.test.ts new file mode 100644 index 0000000000..2dba534257 --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/where-deprecated.int.test.ts @@ -0,0 +1,723 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { TestHelper } from "../../../utils/tests-helper"; + +describe("auth/where - deprecated", () => { + const testHelper = new TestHelper(); + const secret = "secret"; + let User: UniqueType; + let Post: UniqueType; + + beforeEach(() => { + User = testHelper.createUniqueType("User"); + Post = testHelper.createUniqueType("Post"); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("read", () => { + test("should add $jwt.id to where and return user", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${User} {id: "anotherUser"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const users = (gqlResult.data as any)[User.plural] as any[]; + expect(users).toEqual([{ id: userId }]); + }); + + test("should add $jwt.id to where on a relationship", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) + } + + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${Post.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE (p1:${Post} {id: "${postId1}"}) + CREATE (p2:${Post} {id: "${postId2}"}) + MERGE (u)-[:HAS_POST]->(p1) + MERGE (u)-[:HAS_POST]->(p2) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const posts = (gqlResult.data as any)[Post.plural] as any[]; + expect(posts).toHaveLength(2); + const post1 = posts.find((x) => x.id === postId1); + expect(post1).toBeTruthy(); + const post2 = posts.find((x) => x.id === postId2); + expect(post2).toBeTruthy(); + }); + + test("should add $jwt.id to where on a relationship(using connection)", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) + } + + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + const randomPostId = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural}(where: { id_EQ: "${userId}" }) { + postsConnection { + edges { + node { + id + } + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE (p1:${Post} {id: "${postId1}"}) + CREATE (p2:${Post} {id: "${postId2}"}) + CREATE (:${Post} {id: "${randomPostId}"}) + MERGE (u)-[:HAS_POST]->(p1) + MERGE (u)-[:HAS_POST]->(p2) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const posts = (gqlResult.data as any)[User.plural][0].postsConnection as { + edges: { node: { id: string } }[]; + }; + expect(posts.edges).toHaveLength(2); + const post1 = posts.edges.find((x) => x.node.id === postId1); + expect(post1).toBeTruthy(); + const post2 = posts.edges.find((x) => x.node.id === postId2); + expect(post2).toBeTruthy(); + }); + + describe("union", () => { + test("should add $jwt.id to where and return users search", async () => { + const typeDefs = ` + union Content = ${Post} + + type ${User} @node { + id: ID + content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_CONTENT", direction: IN) + } + + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural} { + content { + ... on ${Post} { + id + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE (p1:${Post} {id: "${postId1}"}) + CREATE (p2:${Post} {id: "${postId2}"}) + MERGE (u)-[:HAS_CONTENT]->(p1) + MERGE (u)-[:HAS_CONTENT]->(p2) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + expect(gqlResult.errors).toBeUndefined(); + const posts = (gqlResult.data as any)[User.plural][0].content as any[]; + expect(posts).toHaveLength(2); + const post1 = posts.find((x) => x.id === postId1); + expect(post1).toBeTruthy(); + const post2 = posts.find((x) => x.id === postId2); + expect(post2).toBeTruthy(); + }); + }); + + test("should add $jwt.id to where and return users search(using connections)", async () => { + const typeDefs = ` + union Content = ${Post} + + type ${User} @node { + id: ID + content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_CONTENT", direction: IN) + } + + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + { + ${User.plural} { + contentConnection { + edges { + node { + ... on ${Post} { + id + } + } + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE (p1:${Post} {id: "${postId1}"}) + CREATE (p2:${Post} {id: "${postId2}"}) + CREATE (:${Post} {id: randomUUID()}) + MERGE (u)-[:HAS_CONTENT]->(p1) + MERGE (u)-[:HAS_CONTENT]->(p2) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + expect(gqlResult.errors).toBeUndefined(); + const posts = (gqlResult.data as any)[User.plural][0].contentConnection as { + edges: { node: { id: string } }[]; + }; + expect(posts.edges).toHaveLength(2); + const post1 = posts.edges.find((x) => x.node.id === postId1); + expect(post1).toBeTruthy(); + const post2 = posts.edges.find((x) => x.node.id === postId2); + expect(post2).toBeTruthy(); + }); + }); + + describe("update", () => { + test("should add $jwt.id to where", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(filter: [{ operations: [UPDATE], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + const newUserId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation { + ${User.operations.update}(update: { id_SET: "${newUserId}" }){ + ${User.plural} { + id + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const users = (gqlResult.data as any)[User.operations.update][User.plural] as any[]; + expect(users).toEqual([{ id: newUserId }]); + }); + }); + + describe("delete", () => { + test("should add $jwt.id to where", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(filter: [{ operations: [DELETE], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.delete}(where: { id_EQ: "${userId}" }){ + nodesDeleted + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const nodesDeleted = (gqlResult.data as any)[User.operations.delete].nodesDeleted as number; + expect(nodesDeleted).toBe(1); + + const reQuery = await testHelper.executeCypher(` + MATCH (u:${User} {id: "${userId}"}) + RETURN u + `); + expect(reQuery.records).toHaveLength(0); + }); + }); + + describe("connect", () => { + test("should add jwt.id to where - update update", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.plural} { + id + posts { + id + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const users = (gqlResult.data as any)[User.operations.update][User.plural] as any[]; + expect(users).toEqual([{ id: userId, posts: [{ id: postId }] }]); + }); + + test("should add jwt.id to where - update connect", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + const postId = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(update:{posts:{connect:{where:{node:{id_EQ: "${postId}"}}}}}) { + ${User.plural} { + id + posts { + id + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}"}) + CREATE (:${Post} {id: "${postId}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const users = (gqlResult.data as any)[User.operations.update][User.plural] as any[]; + expect(users).toEqual([{ id: userId, posts: [{ id: postId }] }]); + }); + }); + + describe("disconnect", () => { + test("should add $jwt.id to where (update update)", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(update: { posts: { disconnect: { where: { node: { id_EQ: "${postId1}" } } } } }) { + ${User.plural} { + id + posts { + id + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE (u)-[:HAS_POST]->(:${Post} {id: "${postId1}"}) + CREATE (u)-[:HAS_POST]->(:${Post} {id: "${postId2}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const users = (gqlResult.data as any)[User.operations.update][User.plural] as any[]; + expect(users).toEqual([{ id: userId, posts: [{ id: postId2 }] }]); + }); + + test("should add $jwt.id to where (update disconnect)", async () => { + const typeDefs = ` + type ${User} @node { + id: ID + posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + type ${Post} @node { + id: ID + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + const postId1 = generate({ + charset: "alphabetic", + }); + const postId2 = generate({ + charset: "alphabetic", + }); + + const query = ` + mutation { + ${User.operations.update}(update: { posts: { disconnect: { where: {node: { id_EQ: "${postId1}"}}}}}) { + ${User.plural} { + id + posts { + id + } + } + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (u:${User} {id: "${userId}"}) + CREATE(u)-[:HAS_POST]->(:${Post} {id: "${postId1}"}) + CREATE(u)-[:HAS_POST]->(:${Post} {id: "${postId2}"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + const users = (gqlResult.data as any)[User.operations.update][User.plural] as any[]; + expect(users).toEqual([{ id: userId, posts: [{ id: postId2 }] }]); + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/implicit-count-eq.int.test.ts b/packages/graphql/tests/integration/deprecations/implicit-count-eq.int.test.ts index 3d21acc54e..e9906855f3 100644 --- a/packages/graphql/tests/integration/deprecations/implicit-count-eq.int.test.ts +++ b/packages/graphql/tests/integration/deprecations/implicit-count-eq.int.test.ts @@ -60,7 +60,7 @@ describe("aggregations-where-count", () => { const query = /* GraphQL */ ` { - ${Post.plural}(where: { title_EQ: "hello world", likesAggregate: { count: 2 } }) { + ${Post.plural}(where: { title_EQ: "hello world", likesAggregate: { count_EQ: 2 } }) { title likes { name diff --git a/packages/graphql/tests/integration/deprecations/implicit-eq-filtering.int.test.ts b/packages/graphql/tests/integration/deprecations/implicit-eq-filtering.int.test.ts deleted file mode 100644 index 667acd2208..0000000000 --- a/packages/graphql/tests/integration/deprecations/implicit-eq-filtering.int.test.ts +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import neo4jDriver from "neo4j-driver"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Implicit _EQ filtering", () => { - const testHelper = new TestHelper(); - - afterEach(async () => { - await testHelper.close(); - }); - - test.each([ - ["String", ["Matrix", "Matrix 2", "Matrix 3"]], - ["ID", ["z121039", "a10239", "b12389"]], - ] as const)("the deprecated implicit _EQ should correctly apply filters for string types", async (type, values) => { - const randomType = testHelper.createUniqueType("Movie"); - - const typeDefs = /* GraphQL */ ` - type ${randomType.name} @node { - property: ${type} - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const value = values[0]; - const otherValue1 = values[1]; - const otherValue2 = values[2]; - - await testHelper.executeCypher( - ` - CREATE (:${randomType.name} { property: $value }) - CREATE (:${randomType.name} { property: $otherValue1 }) - CREATE (:${randomType.name} { property: $otherValue2 }) - `, - { value, otherValue1, otherValue2 } - ); - - const query = /* GraphQL */ ` - { - ${randomType.plural}(where: { property: "${value}" }) { - property - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); - }); - - test.each([ - ["Int", [1999999, 1000000, 2999999]], - ["Float", [1.19, 1.2, 2.3]], - ] as const)( - "the deprecated implicit _EQ should correctly apply filters for numerical types", - async (type, values) => { - const randomType = testHelper.createUniqueType("Movie"); - - const typeDefs = /* GraphQL */ ` - type ${randomType.name} @node { - property: ${type} - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const value = values[0]; - const otherValue1 = values[1]; - const otherValue2 = values[2]; - - await testHelper.executeCypher( - ` - CREATE (:${randomType.name} { property: $value }) - CREATE (:${randomType.name} { property: $otherValue1 }) - CREATE (:${randomType.name} { property: $otherValue2 }) - `, - { value, otherValue1, otherValue2 } - ); - - const query = /* GraphQL */ ` - { - ${randomType.plural}(where: { property: ${value} }) { - property - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); - } - ); - - test("the deprecated implicit _EQ should correctly apply filters for Date type", async () => { - const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = /* GraphQL */ ` - type ${randomType.name} @node { - property: Date - } - `; - - const date1 = new Date(1716904582368); - const date2 = new Date(1736900000000); - const neoDate1 = neo4jDriver.types.Date.fromStandardDate(date1); - const neoDate2 = neo4jDriver.types.Date.fromStandardDate(date2); - - await testHelper.executeCypher( - ` - CREATE (:${randomType.name} { property: $datetime1}) - CREATE (:${randomType.name} { property: $datetime2}) - `, - { datetime1: neoDate1, datetime2: neoDate2 } - ); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const query = /* GraphQL */ ` - { - ${randomType.plural}(where: { property: "${neoDate1.toString()}" }) { - property - } - } - `; - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(neoDate1.toString()); - }); - - test("the deprecated implicit _EQ should correctly apply filters for BigInt type", async () => { - const [type, values] = ["BigInt", [1999999, 1000000, 2999999]] as const; - const randomType = testHelper.createUniqueType("Movie"); - - const typeDefs = /* GraphQL */ ` - type ${randomType.name} @node { - property: ${type} - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const value = values[0]; - const otherValue1 = values[1]; - const otherValue2 = values[2]; - - await testHelper.executeCypher( - ` - CREATE (:${randomType.name} { property: $value }) - CREATE (:${randomType.name} { property: $otherValue1 }) - CREATE (:${randomType.name} { property: $otherValue2 }) - `, - { value, otherValue1, otherValue2 } - ); - - const query = /* GraphQL */ ` - { - ${randomType.plural}(where: { property: "${value}" }) { - property - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value.toString()); - }); - - test("implicit _EQ on relationship filter", async () => { - const movieType = testHelper.createUniqueType("Movie"); - const actorType = testHelper.createUniqueType("Actor"); - - const typeDefs = /* GraphQL */ ` - type ${movieType} @node { - title: String - } - - type ${actorType} @node { - name: String - movies: [${movieType}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - role: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher( - ` - CREATE (m1:${movieType.name} { title: "The Matrix" }) - CREATE (m2:${movieType.name} { title: "The Matrix 2" }) - CREATE (m3:${movieType.name} { title: "The Matrix 3" }) - CREATE (k:${actorType.name} { name: "Keanu Reeves" }) - CREATE (c:${actorType.name} { name: "Carrie-Anne Moss" }) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m1) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m2) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m3) - CREATE (c)-[:ACTED_IN { role: "Trinity" }]->(m1) - `, - {} - ); - - const query = /* GraphQL */ ` - { - ${actorType.plural}(where: { movies_SOME: { title: "The Matrix 2" }}) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[actorType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[actorType.plural][0].name).toBe("Keanu Reeves"); - }); - - test("implicit _EQ on connection filter", async () => { - const movieType = testHelper.createUniqueType("Movie"); - const actorType = testHelper.createUniqueType("Actor"); - - const typeDefs = /* GraphQL */ ` - type ${movieType} @node { - title: String - } - - type ${actorType} @node { - name: String - movies: [${movieType}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - role: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher( - ` - CREATE (m1:${movieType.name} { title: "The Matrix" }) - CREATE (m2:${movieType.name} { title: "The Matrix 2" }) - CREATE (m3:${movieType.name} { title: "The Matrix 3" }) - CREATE (k:${actorType.name} { name: "Keanu Reeves" }) - CREATE (c:${actorType.name} { name: "Carrie-Anne Moss" }) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m1) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m2) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m3) - CREATE (c)-[:ACTED_IN { role: "Trinity" }]->(m1) - `, - {} - ); - - const query = /* GraphQL */ ` - { - ${actorType.plural}(where: { moviesConnection_SOME: { node: { title: "The Matrix 2" } }}) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[actorType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[actorType.plural][0].name).toBe("Keanu Reeves"); - }); - - test("implicit _EQ on relationship properties filter", async () => { - const movieType = testHelper.createUniqueType("Movie"); - const actorType = testHelper.createUniqueType("Actor"); - - const typeDefs = /* GraphQL */ ` - type ${movieType} @node { - title: String - } - - type ${actorType} @node { - name: String - movies: [${movieType}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - role: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher( - ` - CREATE (m1:${movieType.name} { title: "The Matrix" }) - CREATE (m2:${movieType.name} { title: "The Matrix 2" }) - CREATE (m3:${movieType.name} { title: "The Matrix 3" }) - CREATE (k:${actorType.name} { name: "Keanu Reeves" }) - CREATE (c:${actorType.name} { name: "Carrie-Anne Moss" }) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m1) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m2) - CREATE (k)-[:ACTED_IN { role: "Neo" }]->(m3) - CREATE (c)-[:ACTED_IN { role: "Trinity" }]->(m1) - `, - {} - ); - - const query = /* GraphQL */ ` - { - ${actorType.plural}(where: { moviesConnection_SOME: { edge: { role: "Trinity" } }}) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)[actorType.plural]).toHaveLength(1); - expect((gqlResult.data as any)[actorType.plural][0].name).toBe("Carrie-Anne Moss"); - }); -}); diff --git a/packages/graphql/tests/integration/deprecations/math-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/math-deprecated.int.test.ts new file mode 100644 index 0000000000..b47e239b0a --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/math-deprecated.int.test.ts @@ -0,0 +1,687 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLError } from "graphql"; +import { int } from "neo4j-driver"; +import { generate } from "randomstring"; +import { TestHelper } from "../../utils/tests-helper"; + +describe("Mathematical operations tests", () => { + const testHelper = new TestHelper(); + const largestSafeSigned32BitInteger = Number(2 ** 31 - 1); + const largestSafeSigned64BitBigInt = BigInt(2 ** 63 - 1).toString(); + + afterEach(async () => { + await testHelper.close(); + }); + + test.each([ + { initialValue: int(0), value: 5, type: "Int", operation: "INCREMENT", expected: 5 }, + { initialValue: int(10), value: 5, type: "Int", operation: "DECREMENT", expected: 5 }, + { initialValue: int(0), value: "5", type: "BigInt", operation: "INCREMENT", expected: "5" }, + { initialValue: int(10), value: "5", type: "BigInt", operation: "DECREMENT", expected: "5" }, + { initialValue: int(10), value: "-5", type: "BigInt", operation: "DECREMENT", expected: "15" }, + { initialValue: 0.0, value: 5.0, type: "Float", operation: "ADD", expected: 5.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "SUBTRACT", expected: 5.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "MULTIPLY", expected: 50.0 }, + { initialValue: 10.0, value: -5.0, type: "Float", operation: "MULTIPLY", expected: -50.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "DIVIDE", expected: 2.0 }, + ])( + "Simple operations on numerical fields: on $type, $operation($initialValue, $value) should return $expected", + async ({ initialValue, type, value, operation, expected }) => { + const movie = testHelper.createUniqueType("Movie"); + + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + id: ID! + viewers: ${type}! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: ${type}) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_${operation}: $value}) { + ${movie.plural} { + id + viewers + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (:${movie.name} {id: $id, viewers: $initialViewers}) + `, + { + id, + initialViewers: initialValue, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value }, + }); + + expect(gqlResult.errors).toBeUndefined(); + expect(gqlResult?.data?.[movie.operations.update]).toEqual({ + [movie.plural]: [{ id, viewers: expected }], + }); + } + ); + + test.each([ + { + initialValue: int(largestSafeSigned32BitInteger), + value: largestSafeSigned32BitInteger, + type: "Int", + operation: "INCREMENT", + expectedError: "overflow", + }, + { + initialValue: int(largestSafeSigned64BitBigInt), + value: largestSafeSigned64BitBigInt, + type: "BigInt", + operation: "INCREMENT", + expectedError: "overflow", + }, + { + initialValue: Number.MAX_VALUE, + value: Number.MAX_VALUE, + type: "Float", + operation: "ADD", + expectedError: "overflow", + }, + { initialValue: 10.0, value: 0.0, type: "Float", operation: "DIVIDE", expectedError: "division by zero" }, + ])( + "Should raise an error in case of $expectedError on $type, initialValue: $initialValue, value: $value", + async ({ initialValue, type, value, operation, expectedError }) => { + const movie = testHelper.createUniqueType("Movie"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + id: ID! + viewers: ${type}! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: ${type}) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_${operation}: $value}) { + ${movie.plural} { + id + viewers + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (:${movie.name} {id: $id, viewers: $initialViewers}) + `, + { + id, + initialViewers: initialValue, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value }, + }); + expect(gqlResult.errors).toBeDefined(); + expect( + (gqlResult.errors as GraphQLError[]).some((el) => el.message.toLowerCase().includes(expectedError)) + ).toBeTruthy(); + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${movie.name} {id: $id}) RETURN n.viewers AS viewers + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toEqual(initialValue); + } + ); + + test("Should raise an error if the input fields are ambiguous", async () => { + const initialViewers = int(100); + const movie = testHelper.createUniqueType("Movie"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + id: ID! + viewers: Int! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: Int) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_SET: $value, viewers_INCREMENT: $value}) { + ${movie.plural} { + id + viewers + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (:${movie.name} {id: $id, viewers: $initialViewers}) + `, + { + id, + initialViewers, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value: 10 }, + }); + + expect(gqlResult.errors).toEqual([ + new GraphQLError(`Conflicting modification of [[viewers_SET]], [[viewers_INCREMENT]] on type ${movie}`), + ]); + + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${movie.name} {id: $id}) RETURN n.viewers AS viewers + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toEqual(initialViewers); + }); + + test("Should be possible to do multiple operations in the same mutation", async () => { + const initialViewers = int(100); + const initialLength = int(100); + const movie = testHelper.createUniqueType("Movie"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + id: ID! + viewers: Int! + length: Int! + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: Int) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {length_DECREMENT: $value, viewers_INCREMENT: $value}) { + ${movie.plural} { + id + viewers + length + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (:${movie.name} {id: $id, viewers: $initialViewers, length: $initialLength}) + `, + { + id, + initialViewers, + initialLength, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value: 10 }, + }); + + expect(gqlResult.errors).toBeUndefined(); + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${movie.name} {id: $id}) RETURN n.viewers AS viewers, n.length AS length + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toEqual(int(110)); + expect(storedValue.records[0]?.get("length")).toEqual(int(90)); + }); + + test("Should be possible to update nested nodes", async () => { + const initialViewers = int(100); + const name = "Luigino"; + const movie = testHelper.createUniqueType("Movie"); + const actor = testHelper.createUniqueType("Actor"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + viewers: Int! + workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) + } + type ${actor.name} @node { + id: ID! + name: String! + worksInMovies: [${movie.name}!]! @relationship(type: "WORKED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: Int) { + ${actor.operations.update}(where: { id_EQ: $id }, + update: { + worksInMovies: [ + { + update: { + node: { + viewers_INCREMENT: $value + } + } + } + ] + } + ) { + ${actor.plural} { + name + worksInMovies { + viewers + } + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (a:${movie.name} {viewers: $initialViewers}), (b:${actor.name} {id: $id, name: $name}) WITH a,b CREATE (a)<-[worksInMovies: WORKED_IN]-(b) RETURN a, worksInMovies, b + `, + { + id, + initialViewers, + name, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value: 10 }, + }); + + expect(gqlResult.errors).toBeUndefined(); + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${actor.name} {id: $id})--(m:${movie.name}) RETURN n.name AS name, m.viewers AS viewers + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toEqual(int(110)); + expect(storedValue.records[0]?.get("name")).toBe(name); + }); + + test("Should be possible to update nested nodes using interfaces", async () => { + const initialViewers = int(100); + const name = "Luigino"; + const movie = testHelper.createUniqueType("Movie"); + const production = testHelper.createUniqueType("Production"); + const actor = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + interface ${production.name} { + viewers: Int! + } + type ${movie.name} implements ${production.name} @node { + viewers: Int! + workers: [${actor.name}!]! @relationship(type: "WORKED_IN", direction: IN) + } + type ${actor.name} @node { + id: ID! + name: String! + worksInProductions: [${production.name}!]! @relationship(type: "WORKED_IN", direction: OUT) + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: Int) { + ${actor.operations.update}(where: { id_EQ: $id }, + update: { + worksInProductions: [ + { + update: { + node: { + viewers_INCREMENT: $value + } + } + } + ] + } + ) { + ${actor.plural} { + name + worksInProductions { + viewers + } + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (a:${movie.name} {viewers: $initialViewers}), (b:${actor.name} {id: $id, name: $name}) WITH a,b CREATE (a)<-[worksInProductions: WORKED_IN]-(b) RETURN a, worksInProductions, b + `, + { + id, + initialViewers, + name, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value: 10 }, + }); + + expect(gqlResult.errors).toBeUndefined(); + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${actor.name} {id: $id})--(m:${movie.name}) RETURN n.name AS name, m.viewers AS viewers + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toEqual(int(110)); + expect(storedValue.records[0]?.get("name")).toBe(name); + }); + + test("Should throws an error if the property holds Nan values", async () => { + const increment = 10; + const movie = testHelper.createUniqueType("Movie"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + id: ID! + viewers: Int + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation($id: ID, $value: Int) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_INCREMENT: $value}) { + ${movie.plural} { + id + viewers + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (:${movie.name} {id: $id}) + `, + { + id, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, value: increment }, + }); + + expect(gqlResult.errors).toBeDefined(); + expect( + (gqlResult.errors as GraphQLError[]).some((el) => + el.message.includes(`Cannot _INCREMENT ${increment} to Nan`) + ) + ).toBeTruthy(); + const storedValue = await testHelper.executeCypher( + ` + MATCH (n:${movie.name} {id: $id}) RETURN n.viewers AS viewers + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("viewers")).toBeNull(); + }); + + test("Should be possible to update relationship properties", async () => { + const initialPay = 100; + const payIncrement = 50; + const movie = testHelper.createUniqueType("Movie"); + const actor = testHelper.createUniqueType("Actor"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + title: String + actors: [${actor.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${actor.name} @node { + id: ID! + name: String! + actedIn: [${movie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + pay: Float + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation Mutation($id: ID, $payIncrement: Float) { + ${actor.operations.update}(where: { id_EQ: $id }, update: { + actedIn: [ + { + update: { + edge: { + pay_ADD: $payIncrement + } + } + } + ] + }) { + ${actor.plural} { + name + actedIn { + title + } + actedInConnection { + edges { + properties { + pay + } + } + } + } + } + } + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (a:${movie.name} {title: "The Matrix"}), (b:${actor.name} {id: $id, name: "Keanu"}) WITH a,b CREATE (a)<-[actedIn: ACTED_IN{ pay: $initialPay }]-(b) RETURN a, actedIn, b + `, + { + id, + initialPay, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, payIncrement }, + }); + + expect(gqlResult.errors).toBeUndefined(); + const storedValue = await testHelper.executeCypher( + ` + MATCH(b: ${actor.name}{id: $id}) -[c: ACTED_IN]-> (a: ${movie.name}) RETURN c.pay as pay + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("pay")).toEqual(initialPay + payIncrement); + }); + + test("Should raise in case of ambiguous properties on relationships", async () => { + const initialPay = 100; + const payIncrement = 50; + const movie = testHelper.createUniqueType("Movie"); + const actor = testHelper.createUniqueType("Actor"); + const typeDefs = /* GraphQL */ ` + type ${movie.name} @node { + title: String + viewers: Int + actors: [${actor.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${actor.name} @node { + id: ID! + name: String! + actedIn: [${movie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + pay: Float + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const id = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + mutation Mutation($id: ID, $payIncrement: Float) { + ${actor.operations.update}(where: { id_EQ: $id }, update: { + actedIn: [ + { + update: { + edge: { + pay_ADD: $payIncrement + pay_SET: $payIncrement + } + } + } + ] + }) { + ${actor.plural} { + name + actedIn { + title + } + actedInConnection { + edges { + properties { + pay + } + } + } + } + } + } + + `; + + // Create new movie + await testHelper.executeCypher( + ` + CREATE (a:${movie.name} {title: "The Matrix"}), (b:${actor.name} {id: $id, name: "Keanu"}) WITH a,b CREATE (a)<-[actedIn: ACTED_IN{ pay: $initialPay }]-(b) RETURN a, actedIn, b + `, + { + id, + initialPay, + } + ); + // Update movie + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { id, payIncrement }, + }); + + expect(gqlResult.errors).toBeDefined(); + + const relationshipType = `${movie.name}ActorsRelationship`; + expect(gqlResult.errors).toEqual([ + new GraphQLError(`Conflicting modification of [[pay_SET]], [[pay_ADD]] on type ${relationshipType}`), + ]); + const storedValue = await testHelper.executeCypher( + ` + MATCH(b: ${actor.name}{id: $id}) -[c: ACTED_IN]-> (a: ${movie.name}) RETURN c.pay as pay + `, + { + id, + } + ); + expect(storedValue.records[0]?.get("pay")).toEqual(initialPay); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/point-cartesian-array-filter.int.test.ts b/packages/graphql/tests/integration/deprecations/point-cartesian-array-filter.int.test.ts new file mode 100644 index 0000000000..6f9a0a3c1a --- /dev/null +++ b/packages/graphql/tests/integration/deprecations/point-cartesian-array-filter.int.test.ts @@ -0,0 +1,130 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../utils/graphql-types"; +import { TestHelper } from "../../utils/tests-helper"; + +describe("[CartesianPoint] - deprecated filters", () => { + const testHelper = new TestHelper(); + let Part: UniqueType; + + beforeEach(async () => { + Part = testHelper.createUniqueType("Part"); + const typeDefs = /* GraphQL */ ` + type ${Part} @node { + id: String! + locations: [CartesianPoint!]! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("enables query of a node with multiple cartesian points", async () => { + const id = "5ba92bc4-95e7-4361-857c-60edcd771391"; + const locations = [...new Array(8)].map(() => ({ + x: 0.02772025833837688, + y: 0.07264417805708945, + })); + + await testHelper.executeCypher( + ` + CALL { + CREATE (r:${Part}) + SET r.id = $id + SET r.locations = [p in $locations | point(p)] + RETURN r + } + + RETURN r { .id, .locations } AS r + `, + { id, locations } + ); + + const partsQuery = /* GraphQL */ ` + query Parts($id: String!) { + ${Part.plural}(where: { id_EQ: $id }) { + id + locations { + y + x + z + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(partsQuery, { variableValues: { id } }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.plural][0]).toEqual({ + id, + locations: locations.map((location) => ({ ...location, z: null, crs: "cartesian" })), + }); + }); + + test("enables query of a node with multiple cartesian-3d points", async () => { + const id = "052322ec-95e5-4b88-8a90-9f0c1df17ee3"; + const locations = [...new Array(8)].map(() => ({ + x: 0.8367510938551277, + y: 0.7110547178890556, + z: 0.9648887133225799, + })); + + await testHelper.executeCypher( + ` + CALL { + CREATE (r:${Part}) + SET r.id = $id + SET r.locations = [p in $locations | point(p)] + RETURN r + } + + RETURN r { .id, .locations } AS r + `, + { id, locations } + ); + + const partsQuery = /* GraphQL */ ` + query Parts($id: String!) { + ${Part.plural}(where: { id_EQ: $id }) { + id + locations { + y + x + z + crs + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(partsQuery, { variableValues: { id } }); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Part.plural][0]).toEqual({ + id, + locations: locations.map((location) => ({ ...location, crs: "cartesian-3d" })), + }); + }); +}); diff --git a/packages/graphql/tests/integration/deprecations/query-direction-deprecated-options.int.test.ts b/packages/graphql/tests/integration/deprecations/query-direction-deprecated-options.int.test.ts deleted file mode 100644 index 143012e4eb..0000000000 --- a/packages/graphql/tests/integration/deprecations/query-direction-deprecated-options.int.test.ts +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("query-direction deprecated options", () => { - const testHelper = new TestHelper(); - let Person: UniqueType; - let stefan: string; - let mike: string; - let charlie: string; - - beforeEach(async () => { - Person = testHelper.createUniqueType("Person"); - stefan = "Stefan"; - mike = "Mike"; - charlie = "Charlie"; - - await testHelper.executeCypher( - ` - CREATE (stefan:${Person} { name: "${stefan}" }) - CREATE (mike:${Person} { name: "${mike}" }) - CREATE (charlie:${Person} { name: "${charlie}" }) - CREATE (stefan)-[:HAS_FRIEND]->(mike) - CREATE (mike)-[:HAS_FRIEND]->(charlie) - ` - ); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should return related node using the queryDirection DIRECTED_ONLY", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DIRECTED_ONLY) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: [], - }, - ]), - }); - }); - - test("should return related node using the queryDirection UNDIRECTED_ONLY", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - ]), - }); - }); - - test("should return related node using the queryDirection DEFAULT_DIRECTED", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: [], - }, - ]), - }); - }); - - test("should return related node using the queryDirection DEFAULT_UNDIRECTED", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - ]), - }); - }); - - test("should return related node using the queryDirection DEFAULT_DIRECTED + directed arg", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends(directed: false) { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - ]), - }); - }); - - test("should return related node using the queryDirection DEFAULT_UNDIRECTED + directed arg", async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - const query = /* GraphQL */ ` - { - ${Person.plural} { - name - friends(directed: true) { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - friends: expect.toIncludeSameMembers([ - { - name: mike, - }, - ]), - }, - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: charlie, - }, - ]), - }, - { - name: charlie, - friends: [], - }, - ]), - }); - }); -}); diff --git a/packages/graphql/tests/integration/deprecations/sort.int.test.ts b/packages/graphql/tests/integration/deprecations/sort.int.test.ts index c14759acf8..cf845d9092 100644 --- a/packages/graphql/tests/integration/deprecations/sort.int.test.ts +++ b/packages/graphql/tests/integration/deprecations/sort.int.test.ts @@ -149,7 +149,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ title: $direction }] } + sort: [{ title: $direction }] ) { id title @@ -186,7 +186,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ title: $direction }] } + sort: [{ title: $direction }] ) { id aliased: title @@ -223,7 +223,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ title: $direction }] } + sort: [{ title: $direction }] ) { id } @@ -266,7 +266,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ numberOfActors: $direction }] } + sort: [{ numberOfActors: $direction }] ) { id numberOfActors @@ -310,7 +310,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ numberOfActors: $direction }] } + sort: [{ numberOfActors: $direction }] ) { id aliased: numberOfActors @@ -355,7 +355,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ numberOfActors: $direction }] } + sort: [{ numberOfActors: $direction }] ) { id } @@ -395,7 +395,7 @@ describe("sort", () => { query ($movieIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}( where: { id_IN: $movieIds }, - options: { sort: [{ numberOfActors: $direction, id: $direction }] } + sort: [{ numberOfActors: $direction, id: $direction }] ) { id } @@ -437,7 +437,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ name: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ name: $direction }]) { id name } @@ -481,7 +481,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ name: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ name: $direction }]) { id aliased: name } @@ -525,7 +525,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ name: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ name: $direction }]) { id } } @@ -571,7 +571,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ totalScreenTime: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ totalScreenTime: $direction }]) { id totalScreenTime } @@ -619,7 +619,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ totalScreenTime: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ totalScreenTime: $direction }]) { id aliased: totalScreenTime } @@ -666,7 +666,7 @@ describe("sort", () => { query($movieId: ID!, $actorIds: [ID!]!, $direction: SortDirection!) { ${movieType.plural}(where: { id_EQ: $movieId }) { id - actors(where: { id_IN: $actorIds }, options: { sort: [{ totalScreenTime: $direction }] }) { + actors(where: { id_IN: $actorIds }, sort: [{ totalScreenTime: $direction }]) { id } } @@ -712,7 +712,7 @@ describe("sort", () => { const query = ` query { ${movieType.plural} { - actors(options: { limit: 1, offset: 1, sort: { name: ASC } }) { + actors(limit: 1, offset: 1, sort: { name: ASC }) { name } } @@ -739,7 +739,7 @@ describe("sort", () => { query ($actorId: ID!, $direction: SortDirection!) { ${actorType.plural}(where: { id_EQ: $actorId }) { id - actedIn(options: { sort: [{ title: $direction }] }) { + actedIn(sort: [{ title: $direction }]) { id title } @@ -785,7 +785,7 @@ describe("sort", () => { query ($actorId: ID!, $direction: SortDirection!) { ${actorType.plural}(where: { id_EQ: $actorId }) { id - actedIn(options: { sort: [{ title: $direction }] }) { + actedIn(sort: [{ title: $direction }]) { id aliased: title } @@ -831,7 +831,7 @@ describe("sort", () => { query ($actorId: ID!, $direction: SortDirection!) { ${actorType.plural}(where: { id_EQ: $actorId }) { id - actedIn(options: { sort: [{ title: $direction }] }) { + actedIn(sort: [{ title: $direction }]) { id } } diff --git a/packages/graphql/tests/integration/deprecations/update-no-implicit-set.int.test.ts b/packages/graphql/tests/integration/deprecations/update-no-implicit-set.int.test.ts deleted file mode 100644 index 8f2157688e..0000000000 --- a/packages/graphql/tests/integration/deprecations/update-no-implicit-set.int.test.ts +++ /dev/null @@ -1,1392 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("update (deprecate implicit _SET)", () => { - const testHelper = new TestHelper(); - let Movie: UniqueType; - let Actor: UniqueType; - let Person: UniqueType; - let Product: UniqueType; - let Photo: UniqueType; - let Color: UniqueType; - let Series: UniqueType; - - beforeEach(() => { - Movie = testHelper.createUniqueType("Movie"); - Actor = testHelper.createUniqueType("Actor"); - Person = testHelper.createUniqueType("Person"); - Product = testHelper.createUniqueType("Product"); - Photo = testHelper.createUniqueType("Photo"); - Color = testHelper.createUniqueType("Color"); - Series = testHelper.createUniqueType("Series"); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should update no movies where predicate yields false", async () => { - const typeDefs = /* GraphQL */ ` - type ${Movie} @node { - id: ID! - name: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id = generate({ - charset: "alphabetic", - }); - - const updatedName = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation($id: ID, $name: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: {name: $name}) { - ${Movie.plural} { - id - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { id, name: updatedName }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ [Movie.plural]: [] }); - }); - - test("should update a single movie", async () => { - const typeDefs = /* GraphQL */ ` - type ${Movie} @node { - id: ID! - name: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id = generate({ - charset: "alphabetic", - }); - - const initialName = generate({ - charset: "alphabetic", - }); - - const updatedName = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation($id: ID, $name: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: {name: $name}) { - ${Movie.plural} { - id - name - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: $id, name: $initialName}) - `, - { - id, - initialName, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { id, name: updatedName }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ [Movie.plural]: [{ id, name: updatedName }] }); - }); - test("should connect through interface relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Movie} implements Production @subscription(events: []) @node { - title: String! - id: ID @unique - director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) - } - - type ${Series} implements Production @node { - title: String! - episode: Int! - id: ID @unique - director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) - } - - interface Production { - id: ID - director: [Creature!]! @declareRelationship - } - - type ${Person} implements Creature @node { - id: ID - movies: Production! @relationship(type: "DIRECTED", direction: OUT) - } - - interface Creature { - id: ID - movies: Production! @declareRelationship - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "1" }, - update: { director: { - connect: { - where: { node: { id_EQ: "2"} }, - connect: { movies: { - where: { node: { id_EQ: "3"} }, - connect: { director: { - where: { node: { id_EQ: "4"} }, - connect: { movies: { - where: { node: { id_EQ: "5" } } - } } - } } - } - } } - } }) { - ${Movie.plural} { - id - title - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: "1", title: "Movie1"}) - CREATE (:${Movie} {id: "3", title: "Movie3"}) - CREATE (:${Movie} {id: "5", title: "Movie5"}) - CREATE (p1:${Person} {id: "2"}) - CREATE (p2:${Person} {id: "4"}) - CREATE (s:${Series} {id: "10", title: "Series1", episode: 20}) - MERGE (p1)-[:DIRECTED]->(s) - MERGE (p2)-[:DIRECTED]->(s) - ` - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - const cypherResult = await testHelper.executeCypher( - ` - MATCH (p:${Person} {id: "4"})-[:DIRECTED]->(m:${Movie} {id: "5"}) RETURN p, m - ` - ); - - expect(cypherResult.records).toHaveLength(1); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: "1", title: "Movie1" }], - }); - }); - - test("should update a movie when matching on relationship property", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const initialMovieId = generate({ - charset: "alphabetic", - }); - - const updatedMovieId = generate({ - charset: "alphabetic", - }); - - const actorName = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation($updatedMovieId: ID, $actorName: String) { - ${Movie.operations.update}( - where: { actorsConnection_SOME: { node: { name_EQ: $actorName } } }, - update: { - id: $updatedMovieId - } - ) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $initialMovieId})<-[:ACTED_IN]-(a:${Actor} {name: $actorName}) - `, - { - initialMovieId, - actorName, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { updatedMovieId, actorName }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: updatedMovieId, actors: [{ name: actorName }] }], - }); - }); - - test("should update 2 movies", async () => { - const typeDefs = /* GraphQL */ ` - type ${Movie} @node { - id: ID! - name: String - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id1 = generate({ - charset: "alphabetic", - }); - - const id2 = generate({ - charset: "alphabetic", - }); - - const updatedName = "Beer"; - - const query = /* GraphQL */ ` - mutation($id1: ID, $id2: ID, $name: String) { - ${Movie.operations.update}(where: { OR: [{ id_EQ: $id1 }, { id_EQ: $id2 }] }, update: {name: $name}) { - ${Movie.plural} { - id - name - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: $id1}) - CREATE (:${Movie} {id: $id2}) - `, - { - id1, - id2, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { id1, id2, name: updatedName }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]?.[Movie.plural] as any[]).toHaveLength(2); - - (gqlResult?.data?.[Movie.operations.update]?.[Movie.plural] as any[]).forEach((movie) => { - expect([id1, id2]).toContain(movie.id); - expect(movie.name).toEqual(updatedName); - }); - }); - - test("should update nested actors from a movie", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const initialName = generate({ - charset: "alphabetic", - }); - - const updatedName = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation($movieId: ID, $initialName: String, $updatedName: String) { - ${Movie.operations.update}( - where: { id_EQ: $movieId }, - update: { - actors: [{ - where: { node: { name_EQ: $initialName } }, - update: { node: { name: $updatedName } } - }] - } - ) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $movieId}) - CREATE (a:${Actor} {name: $initialName}) - MERGE (a)-[:ACTED_IN]->(m) - `, - { - movieId, - initialName, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { movieId, updatedName, initialName }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId, actors: [{ name: updatedName }] }], - }); - }); - - test("should delete a nested actor from a movie abc", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id = generate({ - charset: "alphabetic", - }); - - const actorName1 = generate({ - charset: "alphabetic", - }); - const actorName2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation($id: ID, $actorName1: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: { actors: { delete: { where: { node: { name_EQ: $actorName1 } } } } }) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $id}) - CREATE (a1:${Actor} {name: $actorName1}) - CREATE (a2:${Actor} {name: $actorName2}) - MERGE (a1)-[:ACTED_IN]->(m) - MERGE (a2)-[:ACTED_IN]->(m) - `, - { - id, - actorName1, - actorName2, - } - ); - - const gqlResult = await testHelper.executeGraphQL(mutation, { - variableValues: { id, actorName1 }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id, actors: [{ name: actorName2 }] }], - }); - }); - - test("should delete a nested actor from a movie within an update block", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id = generate({ - charset: "alphabetic", - }); - - const actorName1 = generate({ - charset: "alphabetic", - }); - const actorName2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation($id: ID, $actorName1: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: { actors: { delete: { where: { node: { name_EQ: $actorName1 } } } } }) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $id}) - CREATE (a1:${Actor} {name: $actorName1}) - CREATE (a2:${Actor} {name: $actorName2}) - MERGE (a1)-[:ACTED_IN]->(m) - MERGE (a2)-[:ACTED_IN]->(m) - `, - { - id, - actorName1, - actorName2, - } - ); - - const gqlResult = await testHelper.executeGraphQL(mutation, { - variableValues: { id, actorName1 }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id, actors: [{ name: actorName2 }] }], - }); - }); - - test("should delete a nested actor and one of their nested movies, within an update block abc", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId1 = generate({ - charset: "alphabetic", - }); - - const movieId2 = generate({ - charset: "alphabetic", - }); - - const actorName1 = generate({ - charset: "alphabetic", - }); - - const actorName2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation($movieId1: ID, $actorName1: String, $movieId2: ID) { - ${Movie.operations.update}( - where: { id_EQ: $movieId1 } - update: { - actors: { delete: { where: { node: { name_EQ: $actorName1 } }, delete: { movies: { where: { node: { id_EQ: $movieId2 } } } } } } - } - ) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m1:${Movie} {id: $movieId1}) - CREATE (m2:${Movie} {id: $movieId2}) - - CREATE (a1:${Actor} {name: $actorName1}) - CREATE (a2:${Actor} {name: $actorName2}) - - MERGE (a1)-[:ACTED_IN]->(m1) - MERGE (a1)-[:ACTED_IN]->(m2) - - MERGE (a2)-[:ACTED_IN]->(m1) - MERGE (a2)-[:ACTED_IN]->(m2) - `, - { - movieId1, - actorName1, - actorName2, - movieId2, - } - ); - - const gqlResult = await testHelper.executeGraphQL(mutation, { - variableValues: { movieId1, actorName1, movieId2 }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId1, actors: [{ name: actorName2 }] }], - }); - - const movie2 = await testHelper.executeCypher( - ` - MATCH (m:${Movie} {id: $id}) - RETURN m - `, - { id: movieId2 } - ); - - expect(movie2.records).toHaveLength(0); - }); - - test("should delete multiple nested actors from a movie", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const id = generate({ - charset: "alphabetic", - }); - - const name1 = generate({ - charset: "alphabetic", - }); - - const name2 = generate({ - charset: "alphabetic", - }); - - const name3 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation($id: ID, $name1: String, $name3: String) { - ${Movie.operations.update}( - where: { id_EQ: $id } - update: { actors: { delete: [{ where: { node: { name_EQ: $name1 } } }, { where: { node: { name_EQ: $name3 } } }]}} - ) { - ${Movie.plural} { - id - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $id}) - CREATE (a1:${Actor} {name: $name1}) - CREATE (a2:${Actor} {name: $name2}) - CREATE (a3:${Actor} {name: $name3}) - MERGE (a1)-[:ACTED_IN]->(m) - MERGE (a2)-[:ACTED_IN]->(m) - MERGE (a3)-[:ACTED_IN]->(m) - `, - { - id, - name1, - name2, - name3, - } - ); - - const gqlResult = await testHelper.executeGraphQL(mutation, { - variableValues: { id, name1, name3 }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id, actors: [{ name: name2 }] }], - }); - }); - - test("should update nested actors from a move then update the movie from the nested actors", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - title: String - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" } - update: { - actors: [{ - where: { node: { name_EQ: "old actor name" } } - update: { - node: { - name: "new actor name" - movies: [{ - where: { node: { title_EQ: "old movie title" } } - update: { node: { title: "new movie title" } } - }] - } - } - }] - } - ) { - ${Movie.plural} { - id - title - actors { - name - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: $movieId, title: "old movie title"})<-[:ACTED_IN]-(:${Actor} {name: "old actor name"}) - `, - { - movieId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId, title: "new movie title", actors: [{ name: "new actor name" }] }], - }); - }); - - test("should connect a single movie to a actor", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - id: ID - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const actorId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Movie.operations.update}(where: { id_EQ: "${movieId}" }, update: {actors: { connect: [{ where: { node:{ id_EQ: "${actorId}"}}}]}}) { - ${Movie.plural} { - id - actors { - id - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: $movieId}) - CREATE (:${Actor} {id: $actorId}) - `, - { - movieId, - actorId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId, actors: [{ id: actorId }] }], - }); - }); - - test("should connect a single movie to a actor based on a connection predicate", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - id: ID - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - series: [${Series}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type ${Series} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const actorId = generate({ - charset: "alphabetic", - }); - - const seriesId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation($movieId: ID, $seriesId: ID) { - ${Movie.operations.update}( - where: { id_EQ: $movieId } - update: { actors: { connect: [{ where: { node: { seriesConnection_SOME: { node: { id_EQ: $seriesId } } } } }]} } - ) { - ${Movie.plural} { - id - actors { - id - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Movie} {id: $movieId}) - CREATE (:${Actor} {id: $actorId})-[:ACTED_IN]->(:${Series} {id: $seriesId}) - `, - { - movieId, - actorId, - seriesId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query, { - variableValues: { movieId, seriesId }, - }); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId, actors: [{ id: actorId }] }], - }); - }); - - test("should disconnect an actor from a movie", async () => { - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - id: ID - movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${Movie} @node { - id: ID - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const actorId1 = generate({ - charset: "alphabetic", - }); - const actorId2 = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Movie.operations.update}(where: { id_EQ: "${movieId}" }, update: {actors: { disconnect: [{where: { node: { id_EQ: "${actorId1}"}}}]}}) { - ${Movie.plural} { - id - actors { - id - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (m:${Movie} {id: $movieId}) - CREATE (a1:${Actor} {id: $actorId1}) - CREATE (a2:${Actor} {id: $actorId2}) - MERGE (m)<-[:ACTED_IN]-(a1) - MERGE (m)<-[:ACTED_IN]-(a2) - `, - { - movieId, - actorId1, - actorId2, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ - [Movie.plural]: [{ id: movieId, actors: [{ id: actorId2 }] }], - }); - }); - - test("should disconnect a color from a photo through a product", async () => { - const typeDefs = /* GraphQL */ ` - type ${Product} @node { - id: ID - photos: [${Photo}!]! @relationship(type: "HAS_PHOTO", direction: OUT) - } - - type ${Color} @node { - id: ID - } - - type ${Photo} @node { - id: ID - color: ${Color} @relationship(type: "OF_COLOR", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const productId = generate({ - charset: "alphabetic", - }); - - const photoId = generate({ - charset: "alphabetic", - }); - - const colorId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Product.operations.update}( - where: { id_EQ: "${productId}" } - update: { - photos: [{ - where: { node: { id_EQ: "${photoId}" } } - update: { - node: { - color: { disconnect: { where: { node: { id_EQ: "${colorId}" } } } } - } - } - }] - } - ){ - ${Product.plural} { - id - photos { - id - color { - id - } - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (p:${Product} {id: $productId}) - CREATE (photo:${Photo} {id: $photoId}) - CREATE (color:${Color} {id: $colorId}) - MERGE (p)-[:HAS_PHOTO]->(photo)-[:OF_COLOR]->(color) - - `, - { - productId, - photoId, - colorId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Product.operations.update]).toEqual({ - [Product.plural]: [{ id: productId, photos: [{ id: photoId, color: null }] }], - }); - }); - - test("should update the colors of a product to light versions", async () => { - const typeDefs = /* GraphQL */ ` - type ${Product} @node { - id: ID - name: String - photos: [${Photo}!]! @relationship(type: "HAS_PHOTO", direction: OUT) - } - - - type ${Color} @node { - name: String - id: ID - } - - type ${Photo} @node { - id: ID - name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const productId = generate({ - charset: "alphabetic", - }); - - const photo0Id = generate({ - charset: "alphabetic", - }); - - const photo0Color0Id = generate({ - charset: "alphabetic", - }); - - const photo0Color1Id = generate({ - charset: "alphabetic", - }); - - const photo1Id = generate({ - charset: "alphabetic", - }); - - const photo1Color0Id = generate({ - charset: "alphabetic", - }); - - const photo1Color1Id = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Product.operations.update}( - where: { id_EQ: "${productId}" } - update: { - photos: [ - { - where: { node: { name_EQ: "Green Photo", id_EQ: "${photo0Id}" } } - update: { - node: { - name: "Light Green Photo" - color: { - connect: { where: { node: { name_EQ: "Light Green", id_EQ: "${photo0Color1Id}" } } } - disconnect: { where: { node: { name_EQ: "Green", id_EQ: "${photo0Color0Id}" } } } - } - } - } - } - { - where: { node: { name_EQ: "Yellow Photo", id_EQ: "${photo1Id}" } } - update: { - node: { - name: "Light Yellow Photo" - color: { - connect: { where: { node: { name_EQ: "Light Yellow", id_EQ: "${photo1Color1Id}" } } } - disconnect: { where: { node: { name_EQ: "Yellow", id_EQ: "${photo1Color0Id}" } } } - } - } - } - } - ] - } - ) { - ${Product.plural} { - id - photos { - id - name - color { - id - name - } - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (product:${Product} {name: "Pringles", id: $productId}) - CREATE (photo0:${Photo} {id: $photo0Id, name: "Green Photo"}) - CREATE (photo0_color0:${Color} {id: $photo0_color0Id, name: "Green"}) - CREATE (photo0_color1:${Color} {id: $photo0_color1Id, name: "Light Green"}) - CREATE (photo1:${Photo} {id: $photo1Id, name: "Yellow Photo"}) - CREATE (photo1_color0:${Color} {id: $photo1_color0Id, name: "Yellow"}) - CREATE (photo1_color1:${Color} {id: $photo1_color1Id, name: "Light Yellow"}) - MERGE (product)-[:HAS_PHOTO]->(photo0) - MERGE (photo0)-[:OF_COLOR]->(photo0_color0) - MERGE (product)-[:HAS_PHOTO]->(photo1) - MERGE (photo1)-[:OF_COLOR]->(photo1_color0) - - - `, - { - productId, - photo0Id, - photo0_color0Id: photo0Color0Id, - photo0_color1Id: photo0Color1Id, - photo1Id, - photo1_color0Id: photo1Color0Id, - photo1_color1Id: photo1Color1Id, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect(gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[]).toHaveLength(1); - - const { photos } = (gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[])[0]; - - const greenPhoto = photos.find((x) => x.id === photo0Id); - - expect(greenPhoto).toMatchObject({ - id: photo0Id, - name: "Light Green Photo", - color: { id: photo0Color1Id, name: "Light Green" }, - }); - - const yellowPhoto = photos.find((x) => x.id === photo1Id); - - expect(yellowPhoto).toMatchObject({ - id: photo1Id, - name: "Light Yellow Photo", - color: { id: photo1Color1Id, name: "Light Yellow" }, - }); - }); - - test("should update a Product via creating a new Photo and creating a new Color (via field level update)", async () => { - const typeDefs = /* GraphQL */ ` - type ${Product} @node { - id: ID - name: String - photos: [${Photo}!]! @relationship(type: "HAS_PHOTO", direction: OUT) - } - - - type ${Color} @node { - name: String - id: ID - } - - type ${Photo} @node { - id: ID - name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const productId = generate({ - charset: "alphabetic", - }); - - const photoId = generate({ - charset: "alphabetic", - }); - - const colorId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Product.operations.update}( - where: { id_EQ: "${productId}" } - update: { - photos: [{ - create: [{ - node: { - id: "${photoId}", - name: "Green Photo", - color: { - create: { - node: { - id: "${colorId}", - name: "Green" - } - } - } - } - }] - }] - } - ) { - ${Product.plural} { - id - photos { - id - name - color { - id - name - } - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (product:${Product} {name: "Pringles", id: $productId}) - `, - { - productId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect((gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[])[0]).toMatchObject({ - id: productId, - photos: [{ id: photoId, name: "Green Photo", color: { id: colorId, name: "Green" } }], - }); - }); - - test("should update a Product via creating a new Photo and creating a new Color (via top level create)", async () => { - const typeDefs = /* GraphQL */ ` - type ${Product} @node { - id: ID - name: String - photos: [${Photo}!]! @relationship(type: "HAS_PHOTO", direction: OUT) - } - - - type ${Color} @node { - name: String - id: ID - } - - type ${Photo} @node { - id: ID - name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const productId = generate({ - charset: "alphabetic", - }); - - const photoId = generate({ - charset: "alphabetic", - }); - - const colorId = generate({ - charset: "alphabetic", - }); - - const query = /* GraphQL */ ` - mutation { - ${Product.operations.update}( - where: { id_EQ: "${productId}" } - update: { - photos: { create:[{ - node: { - id: "${photoId}", - name: "Green Photo", - color: { - create: { - node: { - id: "${colorId}", - name: "Green" - } - } - } - } - }]} - } - ) { - ${Product.plural} { - id - photos { - id - name - color { - id - name - } - } - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (product:${Product} {name: "Pringles", id: $productId}) - `, - { - productId, - } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeFalsy(); - - expect((gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[])[0]).toMatchObject({ - id: productId, - photos: [{ id: photoId, name: "Green Photo", color: { id: colorId, name: "Green" } }], - }); - }); -}); diff --git a/packages/graphql/tests/integration/deprecations/update-relationship-no-implicit-set.int.test.ts b/packages/graphql/tests/integration/deprecations/update-relationship-no-implicit-set.int.test.ts deleted file mode 100644 index 3fe2f79f4a..0000000000 --- a/packages/graphql/tests/integration/deprecations/update-relationship-no-implicit-set.int.test.ts +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Update relationship properties (deprecated implicit _SET)", () => { - let testHelper: TestHelper; - let Movie: UniqueType; - let Actor: UniqueType; - let movieTitle: string; - let actor1: string; - let actor2: string; - let actor3: string; - - beforeEach(async () => { - testHelper = new TestHelper(); - Movie = testHelper.createUniqueType("Movie"); - Actor = testHelper.createUniqueType("Actor"); - const typeDefs = /* GraphQL */ ` - type ${Movie} @node { - title: String! - actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${Actor} @node { - name: String! - movies: [${Movie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - `; - movieTitle = generate({ charset: "alphabetic" }); - actor1 = generate({ charset: "alphabetic" }); - actor2 = generate({ charset: "alphabetic" }); - actor3 = generate({ charset: "alphabetic" }); - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher( - ` - CREATE (:${Actor} { name: '${actor1}' })-[:ACTED_IN { screenTime: 105 }]->(m:${Movie} { title: '${movieTitle}'}) - CREATE (m)<-[:ACTED_IN { screenTime: 100 }]-(:${Actor} { name: '${actor2}' }) - ` - ); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update a relationship property on a relationship between two specified nodes (update -> update)", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { title_EQ: "${movieTitle}" } - update: { actors: [{ where: { node: { name_EQ: "${actor1}" } }, update: { edge: { screenTime: 60 } } }] } - ) { - ${Movie.plural} { - title - actorsConnection(sort: { edge: { screenTime: DESC }}) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - expect(result.errors).toBeFalsy(); - - expect((result?.data as any)[Movie.operations.update][Movie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [ - { - properties: { screenTime: 100 }, - node: { - name: actor2, - }, - }, - { - properties: { screenTime: 60 }, - node: { - name: actor1, - }, - }, - ], - }, - }, - ]); - }); - - test("Update properties on both the relationship and end node in a nested update (update -> update)", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { title_EQ: "${movieTitle}" } - update: { - actors: [ - { - where: { node: { name_EQ: "${actor2}" } } - update: { - edge: { screenTime: 60 } - node: { name: "${actor3}" } - } - } - ] - } - ) { - ${Movie.plural} { - title - actorsConnection(sort: { edge: { screenTime: ASC }}) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeFalsy(); - - expect((result?.data as any)[Movie.operations.update][Movie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [ - { - properties: { screenTime: 60 }, - node: { - name: actor3, - }, - }, - { - properties: { screenTime: 105 }, - node: { - name: actor1, - }, - }, - ], - }, - }, - ]); - }); - - test("Create relationship node through update field on end node in a nested update (update -> update)", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { title_EQ: "${movieTitle}" } - update: { - actors: [ - { - create: { - node: { name: "${actor3}" } - edge: { screenTime: 60 } - } - } - ] - } - ) { - ${Movie.plural} { - title - actorsConnection(sort: { edge: { screenTime: ASC }}) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeFalsy(); - - expect((result?.data as any)[Movie.operations.update][Movie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [ - { - properties: { screenTime: 60 }, - node: { - name: actor3, - }, - }, - { - properties: { screenTime: 100 }, - node: { - name: actor2, - }, - }, - { - properties: { screenTime: 105 }, - node: { - name: actor1, - }, - }, - ], - }, - }, - ]); - }); - - test("Create a relationship node with relationship properties on end node in a nested update (update -> create)", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { title_EQ: "${movieTitle}" } - update: { - actors: { - create: { - node: { name: "${actor3}" } - edge: { screenTime: 60 } - } - } - } - ) { - ${Movie.plural} { - title - actorsConnection(sort: { edge: { screenTime: ASC }}) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeFalsy(); - - expect((result?.data as any)[Movie.operations.update][Movie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [ - { - properties: { screenTime: 60 }, - node: { - name: actor3, - }, - }, - { - properties: { screenTime: 100 }, - node: { - name: actor2, - }, - }, - { - properties: { screenTime: 105 }, - node: { - name: actor1, - }, - }, - ], - }, - }, - ]); - }); -}); diff --git a/packages/graphql/tests/integration/directives/alias.int.test.ts b/packages/graphql/tests/integration/directives/alias.int.test.ts index a9f1d4896d..0f544baff8 100644 --- a/packages/graphql/tests/integration/directives/alias.int.test.ts +++ b/packages/graphql/tests/integration/directives/alias.int.test.ts @@ -47,7 +47,7 @@ describe("@alias directive", () => { } type ${AliasDirectiveTestUser} implements AliasInterface @node { - id: ID! @id @unique @alias(property: "dbId") + id: ID! @id @alias(property: "dbId") name: String! @alias(property: "dbName") likes: [${AliasDirectiveTestMovie}!]! @relationship(direction: OUT, type: "LIKES", properties: "AliasDirectiveTestLikesProps") createdAt: DateTime! @timestamp(operations: [CREATE]) @alias(property: "dbCreatedAt") diff --git a/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts b/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts deleted file mode 100644 index 8929c632ff..0000000000 --- a/packages/graphql/tests/integration/directives/alias/connect-or-create.int.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GraphQLError } from "graphql"; -import type { UniqueType } from "../../../utils/graphql-types"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("@alias directive", () => { - const testHelper = new TestHelper(); - - let typeActor: UniqueType; - let typeMovie: UniqueType; - - beforeEach(async () => { - typeMovie = testHelper.createUniqueType("Movie"); - typeActor = testHelper.createUniqueType("Actor"); - - const typeDefs = ` - type ${typeActor} @node { - name: String! - nameAgain: String @alias(property: "name") - movies: [${typeMovie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - - type ${typeMovie} @node { - title: String - titleAgain: String @alias(property: "title") - id: ID! @id @unique - actors: [${typeActor}!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Update mutation with top-level connectOrCreate alias referring to existing field, include both fields as inputs", async () => { - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", titleAgain: "oops" } } - } - } - } - where: { name_EQ: "Tom Hanks"} - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - }); - - test("Create mutation with alias referring to existing field, include only field as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Tom Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "1234" } } - onCreate: { node: { title: "Forrest Gump" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.info.nodesCreated).toBe(2); - }); - test("Create mutation with alias referring to existing field, include only alias as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "2341" } } - onCreate: { node: { titleAgain: "Forrest Run" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.info.nodesCreated).toBe(2); - }); - test("Create mutation with alias referring to existing field, include both fields as inputs", async () => { - const userMutation = ` - mutation { - ${typeActor.operations.create}(input: { - name: "Tom Hanks", - movies: { - connectOrCreate: { - where: { node: { id_EQ: "3412" } } - onCreate: { node: { title: "Forrest Gump", titleAgain: "Forrest G" } } - } - } - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect((gqlResult?.data as any)?.[typeActor.operations.create]?.actors).toBeUndefined(); - }); - - test("Update mutation with alias referring to existing field, include only one field as inputs", async () => { - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}(update: { - name_SET: "a", - movies: [ - { - connectOrCreate: [ - { - onCreate: { - node: { - title: "b" - } - }, - where: { - node: { - id_EQ: "123" - } - } - } - ] - } - ] - }) { - info { - nodesCreated - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - test("Update mutation with alias referring to existing field, include both fields as inputs", async () => { - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}(update: { - name_SET: "a", - movies: [ - { - connectOrCreate: [ - { - onCreate: { - node: { - title: "b", - titleAgain: "bad" - } - }, - where: { - node: { - id_EQ: "123" - } - } - } - ] - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect((gqlResult?.data as any)?.[typeActor.operations.update]?.info).toBeUndefined(); - }); - test("Update mutation nested with create, alias referring to existing field, include both fields as inputs", async () => { - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}(update: { - name_SET: "a", - movies: [ - { - update: { - node: { - title_SET: "b", - actors: [ - { - create: [ - { - node: { - name: "c", - movies: { - connectOrCreate: [ - { - onCreate: { - node: { - title: "d", - titleAgain: "bad" - } - }, - where: { - node: { - id_EQ: "1" - } - } - } - ] - } - } - } - ] - } - ] - } - } - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect((gqlResult?.data as any)?.[typeActor.operations.update]?.info).toBeUndefined(); - }); - test("Update mutation nested with create, alias referring to existing field, include only one field as inputs", async () => { - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}(update: { - name_SET: "a", - movies: [ - { - update: { - node: { - title_SET: "b", - actors: [ - { - create: [ - { - node: { - name: "c", - movies: { - connectOrCreate: [ - { - onCreate: { - node: { - title: "d", - } - }, - where: { - node: { - id_EQ: "1" - } - } - } - ] - } - } - } - ] - } - ] - } - } - } - ] - }) { - info { - nodesCreated - } - } - }`; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/directives/alias/unions.int.test.ts b/packages/graphql/tests/integration/directives/alias/unions.int.test.ts deleted file mode 100644 index 655212472b..0000000000 --- a/packages/graphql/tests/integration/directives/alias/unions.int.test.ts +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { GraphQLError } from "graphql"; -import { gql } from "graphql-tag"; -import type { UniqueType } from "../../../utils/graphql-types"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("@alias directive", () => { - const testHelper = new TestHelper(); - - let typeMovie: UniqueType; - let typeSeries: UniqueType; - let typeActor: UniqueType; - - beforeEach(async () => { - typeMovie = testHelper.createUniqueType("Movie"); - typeSeries = testHelper.createUniqueType("Series"); - typeActor = testHelper.createUniqueType("Actor"); - - const typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - titleAgain: String @alias(property: "title") - isan: String! @unique - } - - type ${typeSeries.name} @node { - title: String! - titleAgain: String @alias(property: "title") - isan: String! @unique - } - - union Production = ${typeMovie.name} | ${typeSeries.name} - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type ${typeActor.name} @node { - name: String! - nameAgain: String @alias(property: "name") - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Create mutation with alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect((gqlResult?.data as any)?.createDirectors?.directors).toBeUndefined(); - }); - test("Create mutation with alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}`), - ]); - expect((gqlResult?.data as any)?.createDirectors?.directors).toBeUndefined(); - }); - test("Create mutation alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = ` - mutation { - ${typeActor.operations.create}( - input: { - name: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}`), - ]); - - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Create mutation with top-level connectOrCreate, alias referring to existing field, include both fields as inputs - update type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - nameAgain_SET: "oops" - name_SET: "Tom Hanks", - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - isan: "${seriesIsan}" - } - } - } - } - } - }){ - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[name_SET]], [[nameAgain_SET]] on type ${typeActor.name}`), - ]); - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - - test("Update mutation alias referring to existing field, include only one field as inputs", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - first rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", titleAgain: "oops", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeMovie.name}`), - ]); - - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - second rel type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks" - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers", - titleAgain: "oops", - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[title]], [[titleAgain]] on type ${typeSeries.name}`), - ]); - - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); - test("Update mutation alias referring to existing field, include both fields as inputs - update type", async () => { - const movieIsan = "0000-0000-03B6-0000-O-0000-0006-P"; - const seriesIsan = "0000-0001-ECC5-0000-8-0000-0001-B"; - - const userMutation = /* GraphQL */ ` - mutation { - ${typeActor.operations.update}( - update: { - name_SET: "Tom Hanks", - nameAgain_SET: "oops", - actedIn: { - ${typeMovie.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${movieIsan}" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "${movieIsan}" } - } - } - } - ${typeSeries.name}: { - connectOrCreate: { - where: { node: { isan_EQ: "${seriesIsan}" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "${seriesIsan}" - } - } - } - } - } - } - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(userMutation); - - expect(gqlResult.errors).toBeDefined(); - expect(gqlResult.errors).toHaveLength(1); - expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[name_SET]], [[nameAgain_SET]] on type ${typeActor.name}`), - ]); - - expect(gqlResult?.data?.[typeActor.operations.update]?.[typeActor.plural]).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/directives/authorization/allow.int.test.ts b/packages/graphql/tests/integration/directives/authorization/allow.int.test.ts index 59ce44f1a0..87bc92f328 100644 --- a/packages/graphql/tests/integration/directives/authorization/allow.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/allow.int.test.ts @@ -129,7 +129,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -183,7 +183,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -241,7 +241,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -251,7 +251,7 @@ describe("auth/allow", () => { } extend type ${postType.name} - @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -297,7 +297,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -307,7 +307,7 @@ describe("auth/allow", () => { } extend type ${postType.name} - @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -358,13 +358,13 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) } @@ -375,7 +375,7 @@ describe("auth/allow", () => { } extend type ${commentType.name} - @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [ { operations: [READ], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -520,7 +520,7 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -576,7 +576,7 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -684,10 +684,10 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID name: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${postType.name} @authorization(validate: [ { operations: [DELETE], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [ { operations: [DELETE], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -743,7 +743,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -751,7 +751,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [ { operations: [DELETE_RELATIONSHIP], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [ { operations: [DELETE_RELATIONSHIP], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -800,13 +800,13 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - post: ${postType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + post: [${postType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) - comments: ${commentType.name}! @relationship(type: "HAS_COMMENT", direction: OUT) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) + comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) } type ${userType.name} @node { @@ -814,7 +814,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [ { operations: [DELETE_RELATIONSHIP], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [ { operations: [DELETE_RELATIONSHIP], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -880,7 +880,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -888,7 +888,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [ { operations: [CREATE_RELATIONSHIP], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [ { operations: [CREATE_RELATIONSHIP], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -938,13 +938,13 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - post: ${postType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + post: [${postType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) - comments: ${commentType.name}! @relationship(type: "HAS_COMMENT", direction: OUT) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) + comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) } type ${userType.name} @node { @@ -952,7 +952,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [ { operations: [CREATE_RELATIONSHIP], when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [ { operations: [CREATE_RELATIONSHIP], when: BEFORE, where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ diff --git a/packages/graphql/tests/integration/directives/authorization/bind.int.test.ts b/packages/graphql/tests/integration/directives/authorization/bind.int.test.ts index bd4e465211..8677555a85 100644 --- a/packages/graphql/tests/integration/directives/authorization/bind.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/bind.int.test.ts @@ -81,7 +81,7 @@ describe("auth/bind", () => { const typeDefs = ` type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } type ${User} @node { @@ -138,7 +138,7 @@ describe("auth/bind", () => { const typeDefs = ` type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } type ${User} @node { @@ -301,7 +301,7 @@ describe("auth/bind", () => { type ${Post} @node { id: ID title: String - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } type ${User} @node { @@ -366,14 +366,14 @@ describe("auth/bind", () => { const typeDefs = ` type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } type ${User} @node { id: ID } - extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [CREATE], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [CREATE], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -473,7 +473,7 @@ describe("auth/bind", () => { const typeDefs = /* GraphQL */ ` type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } type ${User} @node { @@ -481,7 +481,7 @@ describe("auth/bind", () => { posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [UPDATE], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [UPDATE], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -591,10 +591,10 @@ describe("auth/bind", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [CREATE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [CREATE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -654,10 +654,10 @@ describe("auth/bind", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [DELETE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(validate: [{ when: AFTER, operations: [DELETE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ diff --git a/packages/graphql/tests/integration/directives/authorization/claim.int.test.ts b/packages/graphql/tests/integration/directives/authorization/claim.int.test.ts index ec6f55b689..86b7f85b4d 100644 --- a/packages/graphql/tests/integration/directives/authorization/claim.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/claim.int.test.ts @@ -43,8 +43,8 @@ describe("auth claim", () => { }); test("should allow checks against standard claim properties when jwt payload is undefined", async () => { - const typeDefs = ` - type ${User} @authorization(validate: [ { operations: [READ], when: BEFORE, where: { jwt: { iss: "test" } } }]) { + const typeDefs = /* GraphQL */ ` + type ${User} @node @authorization(validate: [ { operations: [READ], when: BEFORE, where: { jwt: { iss_EQ: "test" } } }]) { id: ID password: String } @@ -59,7 +59,7 @@ describe("auth claim", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { password @@ -82,12 +82,12 @@ describe("auth claim", () => { }); test("should allow checks against standard claim properties when jwt payload is defined", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { myClaim: String } - type ${User} @authorization(validate: [ { operations: [READ], when: BEFORE, where: { jwt: { iss: "test" } } }]) { + type ${User} @node @authorization(validate: [ { operations: [READ], when: BEFORE, where: { jwt: { iss_EQ: "test" } } }]) { id: ID password: String } @@ -102,7 +102,7 @@ describe("auth claim", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { password diff --git a/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts b/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts index ff0ed88828..84a125cdea 100644 --- a/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/is-authenticated.int.test.ts @@ -50,7 +50,7 @@ describe("auth/is-authenticated", () => { describe("read", () => { test("should throw if not authenticated type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Product} @authentication(operations: [READ]) @node { id: ID name: String @@ -66,7 +66,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${Product.plural} { id @@ -76,17 +76,13 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); test("should not throw if authenticated type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Product} @authentication(operations: [READ]) @node { id: ID name: String @@ -102,7 +98,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${Product.plural} { id @@ -118,11 +114,11 @@ describe("auth/is-authenticated", () => { }); test("should not throw if authenticated with correct role type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${Product} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { + type ${Product} @authentication(operations: [READ], jwt: { roles: { includes: "admin" } }) @node { id: ID name: String } @@ -137,7 +133,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${Product.plural} { id @@ -153,11 +149,12 @@ describe("auth/is-authenticated", () => { }); test("should throw if authenticated with incorrect role type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${Product} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { + + type ${Product} @authentication(operations: [READ], jwt: { roles: { includes: "admin" } }) @node { id: ID name: String } @@ -172,7 +169,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${Product.plural} { id @@ -188,10 +185,10 @@ describe("auth/is-authenticated", () => { }); test("should throw if not authenticated on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID - password: String @authentication(operations: [READ]) + password: String @authentication(operations: [READ]) } `; @@ -204,7 +201,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { password @@ -214,10 +211,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -226,7 +219,7 @@ describe("auth/is-authenticated", () => { describe("create", () => { test("should not throw if authenticated on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @authentication(operations: [CREATE]) @node { id: ID name: String @@ -242,7 +235,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1" }]) { ${User.plural} { @@ -260,12 +253,12 @@ describe("auth/is-authenticated", () => { }); test("should not throw if authenticated with correct role on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${User} @authentication(operations: [CREATE], jwt: {roles_INCLUDES: "admin"}) @node { + type ${User} @authentication(operations: [CREATE], jwt: {roles: { includes: "admin" } }) @node { id: ID name: String } @@ -280,7 +273,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1" }]) { ${User.plural} { @@ -298,7 +291,7 @@ describe("auth/is-authenticated", () => { }); test("should throw if not authenticated on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @authentication(operations: [CREATE]) @node { id: ID name: String @@ -314,7 +307,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1" }]) { ${User.plural} { @@ -326,21 +319,17 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); test("should throw if authenticated with incorrect role type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${User} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + type ${User} @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) @node { id: ID name: String } @@ -355,7 +344,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1" }]) { ${User.plural} { @@ -373,7 +362,7 @@ describe("auth/is-authenticated", () => { }); test("should throw if not authenticated on nested create type", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID name: String @@ -394,7 +383,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -406,17 +395,13 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); test("should throw if authenticated with incorrect role on nested create type", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -427,7 +412,7 @@ describe("auth/is-authenticated", () => { products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) } - type ${Product} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + type ${Product} @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) @node { id: ID } `; @@ -441,7 +426,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -459,7 +444,7 @@ describe("auth/is-authenticated", () => { }); test("should throw if authenticated with incorrect role on nested create type - not unwind-create", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -470,7 +455,7 @@ describe("auth/is-authenticated", () => { products: [${Product}!]! @relationship(type: "HAS_PRODUCT", direction: OUT) } - type ${Product} @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) @node { + type ${Product} @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) @node { id: ID } `; @@ -484,7 +469,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -502,7 +487,7 @@ describe("auth/is-authenticated", () => { }); test("should not throw if authenticated on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID password: String @authentication(operations: [CREATE]) @@ -514,7 +499,7 @@ describe("auth/is-authenticated", () => { features: { authorization: { key: secret } }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ password: "1" }]) { ${User.plural} { @@ -531,14 +516,14 @@ describe("auth/is-authenticated", () => { expect(gqlResult.errors).toBeUndefined(); }); test("should not throw if authenticated with correct role on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } type ${User} @node { id: ID - password: String @authentication(operations: [CREATE], jwt: {roles_INCLUDES: "admin"}) + password: String @authentication(operations: [CREATE], jwt: {roles: { includes: "admin" }}) } `; @@ -547,7 +532,7 @@ describe("auth/is-authenticated", () => { features: { authorization: { key: secret } }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ password: "1" }]) { ${User.plural} { @@ -565,10 +550,10 @@ describe("auth/is-authenticated", () => { }); test("should throw if not authenticated on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID - password: String @authentication(operations: [CREATE]) + password: String @authentication(operations: [CREATE]) } `; @@ -577,7 +562,7 @@ describe("auth/is-authenticated", () => { features: { authorization: { key: secret } }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ password: "1" }]) { ${User.plural} { @@ -589,24 +574,20 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); test("should throw if authenticated with incorrect role on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } type ${User} @node { id: ID - password: String @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + password: String @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) } `; @@ -615,7 +596,7 @@ describe("auth/is-authenticated", () => { features: { authorization: { key: secret } }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ password: "1" }]) { ${User.plural} { @@ -633,14 +614,14 @@ describe("auth/is-authenticated", () => { }); test("should throw if authenticated with incorrect role on field definition - not unwind-create", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } type ${User} @node { id: ID - password: String @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + password: String @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) } `; @@ -649,7 +630,7 @@ describe("auth/is-authenticated", () => { features: { authorization: { key: secret } }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ password: "1" }]) { ${User.plural} { @@ -667,7 +648,7 @@ describe("auth/is-authenticated", () => { }); test("should throw if not authenticated on nested create field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID name: String @@ -675,7 +656,7 @@ describe("auth/is-authenticated", () => { } type ${Product} @node { - id: ID @authentication(operations: [CREATE]) + id: ID @authentication(operations: [CREATE]) } `; @@ -688,7 +669,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -700,17 +681,13 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); test("should throw if authenticated with incorrect role on nested create field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -722,7 +699,7 @@ describe("auth/is-authenticated", () => { } type ${Product} @node { - id: ID @authentication(operations: [CREATE], jwt: { roles_INCLUDES: "admin" }) + id: ID @authentication(operations: [CREATE], jwt: { roles: { includes: "admin" } }) } `; @@ -735,7 +712,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -753,7 +730,7 @@ describe("auth/is-authenticated", () => { }); test("should throw if authenticated with incorrect role on nested create field - not unwind-create", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -778,7 +755,7 @@ describe("auth/is-authenticated", () => { }, }); - const query = ` + const query = /* GraphQL */ ` mutation { ${User.operations.create}(input: [{ id: "1", products: { create: [{ node: { id: "5" } }] } }]) { ${User.plural} { @@ -799,7 +776,7 @@ describe("auth/is-authenticated", () => { describe("update", () => { test("should not throw if authenticated on type definition", async () => { const typeDefs = /* GraphQL */ ` - type ${User} @authentication(operations: [UPDATE]) @node { + type ${User} @authentication(operations: [UPDATE]) @node { id: ID name: String } @@ -837,7 +814,7 @@ describe("auth/is-authenticated", () => { roles: [String!]! } - type ${User} @authentication(operations: [UPDATE], jwt: {roles_INCLUDES: "admin"}) @node { + type ${User} @authentication(operations: [UPDATE], jwt: {roles: { includes: "admin" }}) @node { id: ID name: String } @@ -871,7 +848,7 @@ describe("auth/is-authenticated", () => { test("should throw if not authenticated on type definition", async () => { const typeDefs = /* GraphQL */ ` - type ${User} @authentication(operations: [UPDATE]) @node { + type ${User} @authentication(operations: [UPDATE]) @node { id: ID name: String } @@ -898,10 +875,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -912,7 +885,7 @@ describe("auth/is-authenticated", () => { type JWTPayload @jwt { roles: [String!]! } - type ${User} @authentication(operations: [UPDATE], jwt: { roles_INCLUDES: "admin" }) @node { + type ${User} @authentication(operations: [UPDATE], jwt: { roles: { includes: "admin" } }) @node { id: ID name: String } @@ -942,374 +915,15 @@ describe("auth/is-authenticated", () => { const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should not throw if authenticated on field definition", async () => { - const typeDefs = /* GraphQL */ ` - type ${User} @node { - id: ID - password: String @authentication(operations: [UPDATE]) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(update: { id_SET: "1" }) { - ${User.plural} { - id - } - } - } - `; - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should not throw if authenticated with correct role on field definition", async () => { - const typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${User} @node { - id: ID - password: String @authentication(operations: [UPDATE], jwt: {roles_INCLUDES: "admin"}) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(update: { id_SET: "1" }) { - ${User.plural} { - id - } - } - } - `; - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should throw if not authenticated on field definition", async () => { - const typeDefs = /* GraphQL */ ` - type ${User} @node { - id: ID - password: String @authentication(operations: [UPDATE]) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(update: { password_SET: "1" }) { - ${User.plural} { - password - } - } - } - `; - - const token = "not valid token"; - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should throw if authenticated with incorrect role on field definition", async () => { - const typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - type ${User} @node { - id: ID - password: String @authentication(operations: [UPDATE], jwt: { roles_INCLUDES: "admin" }) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(update: { password_SET: "1" }) { - ${User.plural} { - password - } - } - } - `; - - const token = createBearerToken(secret, { roles: ["not-an-admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - }); - - describe("connect", () => { - test("should not throw if authenticated", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = /* GraphQL */ ` - type ${Post} @node { - id: String - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP]) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should not throw if authenticated with correct role", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should not throw if authenticated with correct role at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - - const query = /* GraphQL */ ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - - test("should throw if not authenticated", async () => { - const Post = testHelper.createUniqueType("Post"); - - const typeDefs = /* GraphQL */ ` - type ${Post} @node { - id: String - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP]) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - const userId = generate({ - charset: "alphabetic", - }); + }); - const postId = generate({ - charset: "alphabetic", - }); + test("should not throw if authenticated on field definition", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + password: String @authentication(operations: [UPDATE]) + } + `; await testHelper.initNeo4jGraphQL({ typeDefs, @@ -1322,7 +936,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(update: { id_SET: "1" }) { ${User.plural} { id } @@ -1330,50 +944,25 @@ describe("auth/is-authenticated", () => { } `; - // missing super-admin - const token = "not valid token"; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + expect(gqlResult.errors).toBeUndefined(); }); - test("should throw if not authenticated at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - + test("should not throw if authenticated with correct role on field definition", async () => { const typeDefs = /* GraphQL */ ` - type ${Post} @node { - id: String - content: String + type JWTPayload @jwt { + roles: [String!]! } - + type ${User} @node { id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + password: String @authentication(operations: [UPDATE], jwt: { roles: { includes: "admin" }}) } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) `; - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - await testHelper.initNeo4jGraphQL({ typeDefs, features: { @@ -1385,7 +974,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(update: { id_SET: "1" }) { ${User.plural} { id } @@ -1393,57 +982,21 @@ describe("auth/is-authenticated", () => { } `; - // missing super-admin - const token = "not valid token"; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; + const token = createBearerToken(secret, { roles: ["super-admin", "admin"] }); const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); + expect(gqlResult.errors).toBeUndefined(); }); - test("should throw if authenticated with incorrect roles", async () => { - const Post = testHelper.createUniqueType("Post"); - + test("should throw if not authenticated on field definition", async () => { const typeDefs = /* GraphQL */ ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${Post} @node { - id: String - content: String - } - type ${User} @node { id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + password: String @authentication(operations: [UPDATE]) } - - extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) `; - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - await testHelper.initNeo4jGraphQL({ typeDefs, features: { @@ -1455,57 +1008,32 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(update: { password_SET: "1" }) { ${User.plural} { - id + password } } } `; - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const token = createBearerToken(secret, { roles: ["not-an-admin"] }); + const token = "not valid token"; const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); }); - test("should throw if authenticated with incorrect roles at nested level", async () => { - const Post = testHelper.createUniqueType("Post"); - + test("should throw if authenticated with incorrect role on field definition", async () => { const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - - type ${Post} @node { - id: String - content: String - } - type ${User} @node { id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) + password: String @authentication(operations: [UPDATE], jwt: { roles: { includes: "admin" } }) } - - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) `; - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - await testHelper.initNeo4jGraphQL({ typeDefs, features: { @@ -1517,19 +1045,14 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(update: { password_SET: "1" }) { ${User.plural} { - id + password } } } `; - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - const token = createBearerToken(secret, { roles: ["not-an-admin"] }); const gqlResult = await testHelper.executeGraphQLWithToken(query, token); @@ -1538,13 +1061,13 @@ describe("auth/is-authenticated", () => { }); }); - describe("connectOrCreate", () => { + describe("connect", () => { test("should not throw if authenticated", async () => { const Post = testHelper.createUniqueType("Post"); const typeDefs = /* GraphQL */ ` type ${Post} @node { - id: String @unique + id: String content: String } @@ -1580,7 +1103,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { ${User.plural} { id } @@ -1609,7 +1132,7 @@ describe("auth/is-authenticated", () => { } type ${Post} @node { - id: String @unique + id: String content: String } @@ -1621,7 +1144,7 @@ describe("auth/is-authenticated", () => { } extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + @authentication(operations: [CREATE_RELATIONSHIP], jwt: {roles: { includes: "admin" }}) extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) `; @@ -1645,7 +1168,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1674,7 +1197,7 @@ describe("auth/is-authenticated", () => { } type ${Post} @node { - id: String @unique + id: String content: String } @@ -1707,7 +1230,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1732,7 +1255,7 @@ describe("auth/is-authenticated", () => { const typeDefs = /* GraphQL */ ` type ${Post} @node { - id: String @unique + id: String content: String } @@ -1768,7 +1291,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1784,10 +1307,6 @@ describe("auth/is-authenticated", () => { CREATE (:${Post} {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -1796,9 +1315,9 @@ describe("auth/is-authenticated", () => { test("should throw if not authenticated at nested level", async () => { const Post = testHelper.createUniqueType("Post"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Post} @node { - id: String @unique + id: String content: String } @@ -1831,7 +1350,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1847,10 +1366,6 @@ describe("auth/is-authenticated", () => { CREATE (:${Post} {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -1865,7 +1380,7 @@ describe("auth/is-authenticated", () => { } type ${Post} @node { - id: String @unique + id: String content: String } @@ -1877,7 +1392,7 @@ describe("auth/is-authenticated", () => { } extend type ${User} - @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles: { includes: "admin" } }) extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP]) `; @@ -1901,7 +1416,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1930,7 +1445,7 @@ describe("auth/is-authenticated", () => { } type ${Post} @node { - id: String @unique + id: String content: String } @@ -1941,7 +1456,7 @@ describe("auth/is-authenticated", () => { posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + extend type ${Post} @authentication(operations: [CREATE_RELATIONSHIP], jwt: { roles: {includes: "admin"} }) `; const userId = generate({ @@ -1963,7 +1478,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -1988,7 +1503,7 @@ describe("auth/is-authenticated", () => { test("should not throw if authenticated", async () => { const Post = testHelper.createUniqueType("Post"); - const typeDefs = /* GraphLQ */ ` + const typeDefs = /* GraphQL */ ` type ${Post} @node { id: String content: String @@ -2024,9 +1539,9 @@ describe("auth/is-authenticated", () => { }, }); - const query = /* GraphLQ */ ` + const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2067,7 +1582,7 @@ describe("auth/is-authenticated", () => { } extend type ${User} - @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles: { includes: "admin" } }) extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) `; @@ -2091,7 +1606,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: {eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: {eq: "${postId}"} } } } } }) { ${User.plural} { id } @@ -2131,7 +1646,7 @@ describe("auth/is-authenticated", () => { posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles_INCLUDES:"admin"}) + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: {roles: { includes: "admin" } }) `; const userId = generate({ @@ -2153,7 +1668,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2214,7 +1729,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2230,10 +1745,6 @@ describe("auth/is-authenticated", () => { CREATE (:${Post} {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2277,7 +1788,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2293,10 +1804,6 @@ describe("auth/is-authenticated", () => { CREATE (:${Post} {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2323,7 +1830,7 @@ describe("auth/is-authenticated", () => { } extend type ${User} - @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles: { includes: "admin" } }) extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP]) `; @@ -2347,7 +1854,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2387,7 +1894,7 @@ describe("auth/is-authenticated", () => { posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles_INCLUDES: "admin" }) + extend type ${Post} @authentication(operations: [DELETE_RELATIONSHIP], jwt: { roles: { includes: "admin" } }) `; const userId = generate({ @@ -2409,7 +1916,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -2433,7 +1940,7 @@ describe("auth/is-authenticated", () => { describe("delete", () => { test("should not throw if authenticated on type definition", async () => { const typeDefs = /* GraphQL */ ` - type ${User} @authentication(operations: [DELETE]) @node { + type ${User} @authentication(operations: [DELETE]) @node { id: ID name: String } @@ -2469,7 +1976,7 @@ describe("auth/is-authenticated", () => { roles: [String!]! } - type ${User} @authentication(operations: [DELETE], jwt: {roles_INCLUDES: "admin"}) @node { + type ${User} @authentication(operations: [DELETE], jwt: {roles: { includes: "admin" }}) @node { id: ID name: String } @@ -2501,7 +2008,7 @@ describe("auth/is-authenticated", () => { test("should throw if not authenticated on type definition", async () => { const typeDefs = /* GraphQL */ ` - type ${User} @authentication(operations: [DELETE]) @node { + type ${User} @authentication(operations: [DELETE]) @node { id: ID name: String } @@ -2526,10 +2033,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2568,7 +2071,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{ posts: {where:{node: { id_EQ: "${postId}"}}} }) { + ${User.operations.delete}(where: {id: { eq: "${userId}" }}, delete:{ posts: { where:{ node: { id: { eq: "${postId}" }}}} }) { nodesDeleted } } @@ -2580,10 +2083,6 @@ describe("auth/is-authenticated", () => { CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:Post {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2595,7 +2094,7 @@ describe("auth/is-authenticated", () => { roles: [String!]! } - type ${User} @authentication(operations: [DELETE], jwt: { roles_INCLUDES: "admin" }) @node { + type ${User} @authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } }) @node { id: ID name: String } @@ -2637,7 +2136,7 @@ describe("auth/is-authenticated", () => { posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) } - type ${Post} @node @authentication(operations: [DELETE], jwt: { roles_INCLUDES: "admin" }) { + type ${Post} @node @authentication(operations: [DELETE], jwt: { roles: { includes: "admin"} }) { id: ID name: String } @@ -2662,7 +2161,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{posts: {where:{node: { id_EQ: "${postId}"}}} }) { + ${User.operations.delete}(where: { id: { eq: "${userId}" } }, delete: { posts: { where: { node: { id: { eq: "${postId}" } } } } }) { nodesDeleted } } @@ -2712,7 +2211,7 @@ describe("auth/is-authenticated", () => { const query = /* GraphQL */ ` mutation { - ${User.operations.delete}(where: {id_EQ: "${userId}"}, delete:{ posts: {where:{node: { id_EQ: "${postId}"}}} }) { + ${User.operations.delete}(where: {id: { eq: "${userId}" }}, delete:{ posts: { where: { node: { id: { eq: "${postId}" } } } } }) { nodesDeleted } } @@ -2724,10 +2223,6 @@ describe("auth/is-authenticated", () => { CREATE (:${User} {id: "${userId}"})-[:HAS_POST]->(:${Post} {id: "${postId}"}) `); - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2783,7 +2278,7 @@ describe("auth/is-authenticated", () => { } type Query { - users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles: { includes: "admin" }}) } `; @@ -2842,10 +2337,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -2863,7 +2354,7 @@ describe("auth/is-authenticated", () => { } type Query { - users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + users: [${User}] @cypher(statement: "MATCH (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles: { includes: "admin" }}) } `; @@ -2939,7 +2430,7 @@ describe("auth/is-authenticated", () => { } type Mutation { - createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles: { includes: "admin" }}) } `; @@ -2998,10 +2489,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -3019,7 +2506,7 @@ describe("auth/is-authenticated", () => { } type Mutation { - createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + createUser: ${User} @cypher(statement: "CREATE (u:${User}) RETURN u", columnName: "u") @authentication(jwt: {roles: { includes: "admin" }}) } `; @@ -3105,7 +2592,7 @@ describe("auth/is-authenticated", () => { id: ID history: [${History}] @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") - @authentication(operations: [READ], jwt: {roles_INCLUDES: "admin"}) + @authentication(operations: [READ], jwt: {roles: { includes: "admin" } }) } `; @@ -3172,10 +2659,6 @@ describe("auth/is-authenticated", () => { const token = "not valid token"; - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); @@ -3197,7 +2680,7 @@ describe("auth/is-authenticated", () => { id: ID history: [${History}] @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${History}) RETURN h", columnName: "h") - @authentication(operations: [READ], jwt: {roles_INCLUDES: "admin"}) + @authentication(operations: [READ], jwt: {roles: { includes: "admin" } }) } `; @@ -3271,7 +2754,7 @@ describe("auth/is-authenticated", () => { name: String! } - type ${Product} @authentication(operations: [READ], jwt: {name_STARTS_WITH: "John"}) @node { + type ${Product} @authentication(operations: [READ], jwt: { name: { startsWith: "John" } }) @node { id: ID name: String } @@ -3313,7 +2796,7 @@ describe("auth/is-authenticated", () => { name: String! } - type ${Product} @authentication(operations: [READ], jwt: {name_STARTS_WITH: "Doe"}) @node { + type ${Product} @authentication(operations: [READ], jwt: {name: { startsWith: "Doe" } }) @node { id: ID name: String } @@ -3569,13 +3052,13 @@ describe("auth/is-authenticated", () => { const query = ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } } } - `; + `; const token = "not valid token"; @@ -3604,7 +3087,7 @@ describe("auth/is-authenticated", () => { const query = ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" }}, update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -3624,103 +3107,7 @@ describe("auth/is-authenticated", () => { expect(gqlResult.errors).toBeUndefined(); }); }); - describe("connectOrCreate", () => { - let Post: UniqueType; - - beforeEach(async () => { - Post = testHelper.createUniqueType("Post"); - - const typeDefs = ` - type ${Post} @node { - id: String @unique - content: String - } - - type ${User} @node { - id: ID - name: String - password: String - posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) - } - extend schema @authentication(operations: [CREATE_RELATIONSHIP]) - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("should throw if not authenticated type definition", async () => { - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - const token = "not valid token"; - - const socket = new Socket({ readable: true }); - const req = new IncomingMessage(socket); - req.headers.authorization = `Bearer ${token}`; - - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Unauthenticated"); - }); - - test("should not throw if authenticated type definition", async () => { - const userId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { connectOrCreate: { where: { node: { id_EQ: "${postId}" } }, onCreate: { node: { id: "${postId}" } } } } }) { - ${User.plural} { - id - } - } - } - `; - - const token = createBearerToken(secret); - await testHelper.executeCypher(` - CREATE (:${User} {id: "${userId}"}) - CREATE (:${Post} {id: "${postId}"}) - `); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - }); - }); describe("disconnect", () => { let Post: UniqueType; @@ -3763,7 +3150,7 @@ describe("auth/is-authenticated", () => { const query = ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -3798,7 +3185,7 @@ describe("auth/is-authenticated", () => { const query = ` mutation { - ${User.operations.update}(where: { id_EQ: "${userId}" }, update: { posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(where: { id: { eq: "${userId}" } }, update: { posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id } @@ -3899,12 +3286,12 @@ describe("auth/is-authenticated", () => { test("should throw if not authenticated type definition", async () => { const query = ` - query { - allUsers { - id + query { + allUsers { + id + } } - } - `; + `; const token = "not valid token"; @@ -3919,12 +3306,12 @@ describe("auth/is-authenticated", () => { test("should not throw if authenticated type definition", async () => { const query = ` - query { - allUsers { - id + query { + allUsers { + id + } } - } - `; + `; const token = createBearerToken(secret); diff --git a/packages/graphql/tests/integration/directives/authorization/object-path.int.test.ts b/packages/graphql/tests/integration/directives/authorization/object-path.int.test.ts index 19d1a5ab02..b2de96e613 100644 --- a/packages/graphql/tests/integration/directives/authorization/object-path.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/object-path.int.test.ts @@ -38,7 +38,7 @@ describe("auth/object-path", () => { }); test("should use object path with allow", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { nestedSub: String! @jwtClaim(path: "nested.object.path.sub") } @@ -47,16 +47,16 @@ describe("auth/object-path", () => { id: ID } - extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { id_EQ: "$jwt.nestedSub" } } }]) + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { id: { eq: "$jwt.nestedSub" } } } }]) `; const userId = generate({ charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { - ${User.plural}(where: {id_EQ: "${userId}"}) { + ${User.plural}(where: { id: { eq: "${userId}" } }) { id } } @@ -94,17 +94,17 @@ describe("auth/object-path", () => { }); test("should use $context value plucking on auth", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID } type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${Post} @node @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator: { id_EQ: "$context.userId" } } } }]) + extend type ${Post} @node @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator: { single: { id: { eq: "$context.userId" } } } } } }]) `; const userId = generate({ @@ -115,9 +115,9 @@ describe("auth/object-path", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { - ${Post.plural}(where: {id_EQ: "${postId}"}) { + ${Post.plural}(where: {id: { eq: "${postId}" }}) { id } } @@ -149,7 +149,7 @@ describe("auth/object-path", () => { }); test("should use object path with roles", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! @jwtClaim(path: "https://github\\\\.com/claims.https://github\\\\.com/claims/roles") } @@ -158,16 +158,16 @@ describe("auth/object-path", () => { id: ID } - extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles: { includes: "admin" } } } }]) `; const userId = generate({ charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { - ${User.plural}(where: {id_EQ: "${userId}"}) { + ${User.plural}(where: { id: { eq: "${userId}" }}) { id } } @@ -199,7 +199,7 @@ describe("auth/object-path", () => { }); test("should use object path with JWT endpoint", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -208,16 +208,16 @@ describe("auth/object-path", () => { id: ID } - extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) + extend type ${User} @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { jwt: { roles:{ includes: "admin" }} } }]) `; const userId = generate({ charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { - ${User.plural}(where: {id_EQ: "${userId}"}) { + ${User.plural}(where: {id: { eq: "${userId}" }}) { id } } diff --git a/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts b/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts index fca4dd93fd..ddf5ae6f00 100644 --- a/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/roles.int.test.ts @@ -29,14 +29,12 @@ describe("auth/roles", () => { let typeUser: UniqueType; let typeProduct: UniqueType; let typePost: UniqueType; - let typeComment: UniqueType; let typeHistory: UniqueType; beforeEach(async () => { typeUser = testHelper.createUniqueType("User"); typeProduct = testHelper.createUniqueType("Product"); typePost = testHelper.createUniqueType("Post"); - typeComment = testHelper.createUniqueType("Comment"); typeHistory = testHelper.createUniqueType("History"); await testHelper.executeCypher( @@ -53,7 +51,7 @@ describe("auth/roles", () => { describe("read", () => { test("should throw if missing role on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -61,19 +59,19 @@ describe("auth/roles", () => { type ${typeProduct} @authorization(validate: [{ when: [BEFORE], operations: [READ], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) @node { id: ID name: String } `; - const query = ` - { - ${typeProduct.plural} { - id + const query = /* GraphQL */ ` + { + ${typeProduct.plural} { + id + } } - } `; await testHelper.initNeo4jGraphQL({ @@ -93,7 +91,7 @@ describe("auth/roles", () => { }); test("should throw if missing role on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -103,7 +101,7 @@ describe("auth/roles", () => { password: String @authorization(validate: [{ when: [BEFORE], operations: [READ], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) } `; @@ -133,7 +131,7 @@ describe("auth/roles", () => { }); test("Read Node & Cypher Field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -142,7 +140,7 @@ describe("auth/roles", () => { url: String @authorization(validate: [{ when: [BEFORE], operations: [READ], - where: { jwt: { roles_INCLUDES: "super-admin" } } + where: { jwt: { roles: { includes: "super-admin" }} } }]) } type ${typeUser} @node { @@ -155,7 +153,7 @@ describe("auth/roles", () => { @authorization(validate: [{ when: [BEFORE], operations: [READ, CREATE, UPDATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, DELETE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" }} } }]) extend type ${typeUser} { @@ -164,12 +162,12 @@ describe("auth/roles", () => { @authorization(validate: [{ when: [BEFORE], operations: [READ], - where: { jwt: { roles_INCLUDES: "super-admin" } } + where: { jwt: { roles: { includes: "super-admin" }} } }]) } `; - const query = ` + const query = /* GraphQL */ ` { ${typeUser.plural} { history { @@ -214,21 +212,22 @@ describe("auth/roles", () => { // This tests reproduces the security issue related to authorization without match #195 // eslint-disable-next-line jest/no-disabled-tests test.skip("should throw if missing role on type definition and no nodes are matched", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type NotANode @authorization(validate: [{ - when: [BEFORE], - operations: [READ], - where: { jwt: { roles_INCLUDES: "admin" } } - }]) { + type NotANode + @authorization( + validate: [ + { when: [BEFORE], operations: [READ], where: { jwt: { roles: { includes: "admin" } } } } + ] + ) { name: String } `; - const query = ` + const query = /* GraphQL */ ` { notANodes { name @@ -255,7 +254,7 @@ describe("auth/roles", () => { describe("create", () => { test("should throw if missing role on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -263,14 +262,14 @@ describe("auth/roles", () => { type ${typeUser} @authorization(validate: [{ when: [AFTER], operations: [CREATE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) @node { id: ID name: String } `; - const query = ` + const query = /* GraphQL */ ` mutation { ${typeUser.operations.create}(input: [{ id: "1" }]) { ${typeUser.plural} { @@ -297,7 +296,7 @@ describe("auth/roles", () => { }); test("should throw if missing role on field definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -307,12 +306,12 @@ describe("auth/roles", () => { password: String @authorization(validate: [{ when: [AFTER], operations: [CREATE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) } `; - const query = ` + const query = /* GraphQL */ ` mutation { ${typeUser.operations.create}(input: [{ password: "1" }]) { ${typeUser.plural} { @@ -339,7 +338,7 @@ describe("auth/roles", () => { }); test("should not throw if missing role on field definition if is not specified in the request", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -349,12 +348,12 @@ describe("auth/roles", () => { password: String @authorization(validate: [{ when: [AFTER], operations: [CREATE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) } `; - const query = ` + const query = /* GraphQL */ ` mutation { ${typeUser.operations.create}(input: [{ id: "1" }]) { ${typeUser.plural} { @@ -391,7 +390,7 @@ describe("auth/roles", () => { type ${typeUser} @authorization(validate: [{ when: [BEFORE], operations: [UPDATE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) @node{ id: ID name: String @@ -435,7 +434,7 @@ describe("auth/roles", () => { password: String @authorization(validate: [{ when: [BEFORE], operations: [UPDATE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) } `; @@ -490,13 +489,13 @@ describe("auth/roles", () => { @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) extend type ${typePost} @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "super-admin" } } + where: { jwt: { roles: { includes: "super-admin" } } } }]) `; @@ -510,7 +509,7 @@ describe("auth/roles", () => { const query = /* GraphQL */ ` mutation { - ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } }}) { ${typeUser.plural} { id } @@ -538,93 +537,6 @@ describe("auth/roles", () => { expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); }); - - test("should throw if missing role on nested connect", async () => { - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeComment} @node { - id: String - content: String - post: ${typePost}! @relationship(type: "HAS_COMMENT", direction: IN) - } - - type ${typePost} @node { - id: String - content: String - creator: ${typeUser}! @relationship(type: "HAS_POST", direction: IN) - comments: [${typeComment}!]! @relationship(type: "HAS_COMMENT", direction: OUT) - } - - type ${typeUser} @node { - id: ID - name: String - posts: [${typePost}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${typeUser} - @authorization(validate: [{ - when: [BEFORE], - operations: [CREATE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "admin" } } - }]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const commentId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${typeComment.operations.update}( - where: { id_EQ: "${commentId}" } - update: { - post: { - update: { - node: { - creator: { connect: { where: { node: { id_EQ: "${userId}" } } } } - } - } - } - } - ) { - ${typeComment.plural} { - content - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - - await testHelper.executeCypher(` - CREATE (:${typeComment} {id: "${commentId}"})<-[:HAS_COMMENT]-(:${typePost} {id: "${postId}"}) - CREATE (:${typeUser} {id: "${userId}"}) - `); - - const token = createBearerToken(secret, { roles: [""] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); }); describe("disconnect", () => { @@ -650,13 +562,13 @@ describe("auth/roles", () => { @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" }} } }]) extend type ${typePost} @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "super-admin" } } + where: { jwt: { roles: { includes: "super-admin" } } } }]) `; @@ -670,7 +582,7 @@ describe("auth/roles", () => { const query = /* GraphQL */ ` mutation { - ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { disconnect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${typeUser.operations.update}(update: { id_SET: "${userId}", posts: { disconnect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${typeUser.plural} { id } @@ -698,97 +610,11 @@ describe("auth/roles", () => { expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); }); - - test("should throw if missing role on nested disconnect", async () => { - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${typeComment} @node { - id: String - content: String - post: ${typePost}! @relationship(type: "HAS_COMMENT", direction: IN) - } - - type ${typePost} @node { - id: String - content: String - creator: ${typeUser}! @relationship(type: "HAS_POST", direction: IN) - comments: [${typeComment}!]! @relationship(type: "HAS_COMMENT", direction: OUT) - } - - type ${typeUser} @node { - id: ID - name: String - posts: [${typePost}!]! @relationship(type: "HAS_POST", direction: OUT) - } - - extend type ${typeUser} - @authorization(validate: [{ - when: [BEFORE], - operations: [DELETE_RELATIONSHIP], - where: { jwt: { roles_INCLUDES: "admin" } } - }]) - `; - - const userId = generate({ - charset: "alphabetic", - }); - - const commentId = generate({ - charset: "alphabetic", - }); - - const postId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${typeComment.operations.update}( - where: { id_EQ: "${commentId}" } - update: { - post: { - update: { - node: { - creator: { disconnect: { where: { node: { id_EQ: "${userId}" } } } } - } - } - } - } - ) { - ${typeComment.plural} { - content - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - - await testHelper.executeCypher(` - CREATE (:${typeComment} {id: "${commentId}"})<-[:HAS_COMMENT]-(:${typePost} {id: "${postId}"})<-[:HAS_POST]-(:${typeUser} {id: "${userId}"}) - `); - - const token = createBearerToken(secret, { roles: [""] }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect((gqlResult.errors as any[])[0].message).toBe("Forbidden"); - }); }); describe("delete", () => { test("should throw if missing role on type definition", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -796,14 +622,14 @@ describe("auth/roles", () => { type ${typeUser} @node @authorization(validate: [{ when: [BEFORE], operations: [DELETE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) { id: ID name: String } `; - const query = ` + const query = /* GraphQL */ ` mutation { ${typeUser.operations.delete} { nodesDeleted @@ -828,7 +654,7 @@ describe("auth/roles", () => { }); test("should throw if missing role on type definition (with nested delete)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -842,7 +668,7 @@ describe("auth/roles", () => { type ${typePost} @node @authorization(validate: [{ when: [BEFORE], operations: [DELETE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) { id: ID name: String @@ -857,9 +683,9 @@ describe("auth/roles", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` mutation { - ${typeUser.operations.delete}(where: {id_EQ: "${userId}"}, delete:{posts: {where:{node: { id_EQ: "${postId}"}}}}) { + ${typeUser.operations.delete}(where: {id: { eq: "${userId}"}}, delete: { posts: { where:{ node: { id: { eq: "${postId}" }}}}}) { nodesDeleted } } @@ -889,7 +715,7 @@ describe("auth/roles", () => { // TODO: Move these checks into JavaScript! Fun! describe("custom-resolvers", () => { test("should throw if missing role on custom Query with @cypher", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -900,7 +726,7 @@ describe("auth/roles", () => { } type Query { - ${typeUser.plural}: [${typeUser}] @cypher(statement: "MATCH (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + ${typeUser.plural}: [${typeUser}] @cypher(statement: "MATCH (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: {roles: { includes: "admin" }}) } `; @@ -929,7 +755,7 @@ describe("auth/roles", () => { }); test("should throw if missing role on custom Mutation with @cypher", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -940,7 +766,7 @@ describe("auth/roles", () => { } type Mutation { - ${typeUser.operations.create}: ${typeUser} @cypher(statement: "CREATE (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: {roles_INCLUDES: "admin"}) + ${typeUser.operations.create}: ${typeUser} @cypher(statement: "CREATE (u:${typeUser}) RETURN u AS u", columnName: "u") @authentication(jwt: { roles: { includes: "admin" } }) } `; @@ -969,7 +795,7 @@ describe("auth/roles", () => { }); test("should throw if missing role on Field definition @cypher", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } @@ -984,12 +810,12 @@ describe("auth/roles", () => { @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:${typeHistory}) RETURN h AS h", columnName: "h") @authorization(validate: [{ when: [BEFORE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } }]) } `; - const query = ` + const query = /* GraphQL */ ` { ${typeUser.plural} { history { @@ -1020,7 +846,7 @@ describe("auth/roles", () => { test("combines where with roles", async () => { const type = testHelper.createUniqueType("User"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { id: String! roles: [String!]! @@ -1036,10 +862,10 @@ describe("auth/roles", () => { @authorization( filter: [ { - where: { node: { id_EQ: "$jwt.id" }, jwt: { roles_INCLUDES: "user" } } + where: { node: { id: { eq: "$jwt.id" } }, jwt: { roles: { includes: "user" } } } }, { - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } } ] ) @@ -1053,7 +879,7 @@ describe("auth/roles", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` query { ${type.plural} { id @@ -1103,7 +929,7 @@ describe("auth/roles", () => { test("can read role from path containing dots", async () => { const type = testHelper.createUniqueType("User"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! @jwtClaim(path: "https://auth0\\\\.mysite\\\\.com/claims.https://auth0\\\\.mysite\\\\.com/claims/roles") } @@ -1119,7 +945,7 @@ describe("auth/roles", () => { validate: [ { when: [BEFORE], - where: { jwt: { roles_INCLUDES: "admin" } } + where: { jwt: { roles: { includes: "admin" } } } } ] ) @@ -1129,7 +955,7 @@ describe("auth/roles", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` query { ${type.plural} { id diff --git a/packages/graphql/tests/integration/directives/authorization/where.int.test.ts b/packages/graphql/tests/integration/directives/authorization/where.int.test.ts index 8e33e52a02..bfce10d846 100644 --- a/packages/graphql/tests/integration/directives/authorization/where.int.test.ts +++ b/packages/graphql/tests/integration/directives/authorization/where.int.test.ts @@ -39,19 +39,19 @@ describe("auth/where", () => { describe("read", () => { test("should add $jwt.id to where and return user", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID } - extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { id @@ -83,8 +83,101 @@ describe("auth/where", () => { expect(users).toEqual([{ id: userId }]); }); + test("should return the correct id that startsWith the '$jwt.sub'", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id: { startsWith: "$jwt.sub" } } } }]) + `; + + const userId = generate({ + charset: "alphabetic", + }); + + const query = /* GraphQL */ ` + { + ${User.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}SomethingMore"}) + CREATE (:${User} {id: "anotherUser"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const users = (gqlResult.data as any)[User.plural] as any[]; + expect(users).toEqual([{ id: `${userId}SomethingMore` }]); + }); + + test("should return the correct id that startsWith the '$jwt.sub' and the '$jwt.sub' should startsWith test", async () => { + const typeDefs = /* GraphQL */ ` + type ${User} @node { + id: ID + } + + extend type ${User} @authorization(filter: [{ operations: [READ], where: { + node: { id: { startsWith: "$jwt.sub" } } + jwt: { sub: { startsWith: "test" } } + } }]) + `; + + const userId = `test${generate({ + charset: "alphabetic", + })}`; + + const query = /* GraphQL */ ` + { + ${User.plural} { + id + } + } + `; + + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + + await testHelper.executeCypher(` + CREATE (:${User} {id: "${userId}SomethingMore"}) + CREATE (:${User} {id: "anotherUser"}) + `); + + const token = createBearerToken(secret, { sub: userId }); + + const gqlResult = await testHelper.executeGraphQLWithToken(query, token); + + expect(gqlResult.errors).toBeUndefined(); + + const users = (gqlResult.data as any)[User.plural] as any[]; + expect(users).toEqual([{ id: `${userId}SomethingMore` }]); + }); + test("should add $jwt.id to where on a relationship", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -92,10 +185,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { single: { id: { eq: "$jwt.sub" } } } } } }]) `; const userId = generate({ @@ -109,7 +202,7 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { ${Post.plural} { id @@ -149,7 +242,7 @@ describe("auth/where", () => { }); test("should add $jwt.id to where on a relationship(using connection)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -157,10 +250,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { single: { id: { eq: "$jwt.sub" } } } } } }]) `; const userId = generate({ @@ -177,9 +270,9 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { - ${User.plural}(where: { id_EQ: "${userId}" }) { + ${User.plural}(where: { id: { eq: "${userId}"} }) { postsConnection { edges { node { @@ -227,7 +320,7 @@ describe("auth/where", () => { describe("union", () => { test("should add $jwt.id to where and return users search", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` union Content = ${Post} type ${User} @node { @@ -237,11 +330,11 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_CONTENT", direction: IN) } - extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) - extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { single: { id: { eq: "$jwt.sub" } } } } } }]) + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -255,7 +348,7 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { content { @@ -298,7 +391,7 @@ describe("auth/where", () => { }); test("should add $jwt.id to where and return users search(using connections)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` union Content = ${Post} type ${User} @node { @@ -308,11 +401,11 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_CONTENT", direction: IN) } - extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) - extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${Post} @authorization(filter: [{ operations: [READ], where: { node: { creator: { single: { id: { eq: "$jwt.sub" } } } } } }]) + extend type ${User} @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -326,7 +419,7 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` { ${User.plural} { contentConnection { @@ -382,7 +475,7 @@ describe("auth/where", () => { id: ID } - extend type ${User} @authorization(filter: [{ operations: [UPDATE], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [UPDATE], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -428,12 +521,12 @@ describe("auth/where", () => { describe("delete", () => { test("should add $jwt.id to where", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID } - extend type ${User} @authorization(filter: [{ operations: [DELETE], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [DELETE], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -442,7 +535,7 @@ describe("auth/where", () => { const query = ` mutation { - ${User.operations.delete}(where: { id_EQ: "${userId}" }){ + ${User.operations.delete}(where: { id: { eq: "${userId}" } }){ nodesDeleted } } @@ -479,7 +572,7 @@ describe("auth/where", () => { describe("connect", () => { test("should add jwt.id to where - update update", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -487,10 +580,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -500,9 +593,9 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` mutation { - ${User.operations.update}(update: { posts: { connect: { where: { node: { id_EQ: "${postId}" } } } } }) { + ${User.operations.update}(update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id posts { @@ -537,7 +630,7 @@ describe("auth/where", () => { }); test("should add jwt.id to where - update connect", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -545,10 +638,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [UPDATE, CREATE_RELATIONSHIP], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -558,9 +651,9 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` mutation { - ${User.operations.update}(update:{posts:{connect:{where:{node:{id_EQ: "${postId}"}}}}}) { + ${User.operations.update}(update: { posts: { connect: { where: { node: { id: { eq: "${postId}" } } } } } }) { ${User.plural} { id posts { @@ -597,7 +690,7 @@ describe("auth/where", () => { describe("disconnect", () => { test("should add $jwt.id to where (update update)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -605,10 +698,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id: { eq: "$jwt.sub" }} } }]) `; const userId = generate({ @@ -621,9 +714,9 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` mutation { - ${User.operations.update}(update: { posts: { disconnect: { where: { node: { id_EQ: "${postId1}" } } } } }) { + ${User.operations.update}(update: { posts: { disconnect: { where: { node: { id: { eq: "${postId1}" } } } } } }) { ${User.plural} { id posts { @@ -659,7 +752,7 @@ describe("auth/where", () => { }); test("should add $jwt.id to where (update disconnect)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${User} @node { id: ID posts: [${Post}!]! @relationship(type: "HAS_POST", direction: OUT) @@ -667,10 +760,10 @@ describe("auth/where", () => { type ${Post} @node { id: ID - creator: ${User}! @relationship(type: "HAS_POST", direction: OUT) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type ${User} @authorization(filter: [{ operations: [UPDATE, DELETE_RELATIONSHIP], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -683,9 +776,9 @@ describe("auth/where", () => { charset: "alphabetic", }); - const query = ` + const query = /* GraphQL */ ` mutation { - ${User.operations.update}(update: { posts: { disconnect: { where: {node: { id_EQ: "${postId1}"}}}}}) { + ${User.operations.update}(update: { posts: { disconnect: { where: { node: { id: { eq: "${postId1}" } } } }} }) { ${User.plural} { id posts { diff --git a/packages/graphql/tests/integration/directives/customResolver.int.test.ts b/packages/graphql/tests/integration/directives/customResolver.int.test.ts index 61b8bbaf46..aaceaec113 100644 --- a/packages/graphql/tests/integration/directives/customResolver.int.test.ts +++ b/packages/graphql/tests/integration/directives/customResolver.int.test.ts @@ -347,12 +347,14 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city }") } `; - const fullNameResolver = ({ firstName, lastName, address }) => `${firstName} ${lastName} from ${address.city}`; + const fullNameResolver = ({ firstName, lastName, address }) => { + return `${firstName} ${lastName} from ${address[0].city}`; + }; const resolvers = { [User.name]: { @@ -379,11 +381,7 @@ describe("Related Fields", () => { expect(result.data as any).toEqual({ [User.plural]: [ { - fullName: fullNameResolver({ - firstName: userInput1.firstName, - lastName: userInput1.lastName, - address: { city: addressInput1.city }, - }), + fullName: "First Last from some city", }, ], }); @@ -405,7 +403,7 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city } doesNotExist") } `; @@ -444,7 +442,7 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city doesNotExist }") } `; @@ -483,12 +481,13 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city }") } `; - const fullNameResolver = ({ firstName, lastName, address }) => `${firstName} ${lastName} from ${address.city}`; + const fullNameResolver = ({ firstName, lastName, address }) => + `${firstName} ${lastName} from ${address[0].city}`; const resolvers = { [User.name]: { @@ -521,11 +520,11 @@ describe("Related Fields", () => { [User.plural]: [ { id: userInput1.id, - address: addressInput1, + address: [addressInput1], fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: addressInput1.city }, + address: [{ city: addressInput1.city }], }), }, ], @@ -551,12 +550,13 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address:[${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city }") } `; - const fullNameResolver = ({ firstName, lastName, address }) => `${firstName} ${lastName} from ${address.city}`; + const fullNameResolver = ({ firstName, lastName, address }) => + `${firstName} ${lastName} from ${address[0].city}`; const resolvers = { [User.name]: { @@ -589,20 +589,20 @@ describe("Related Fields", () => { [User.plural]: expect.toIncludeSameMembers([ { id: userInput1.id, - address: addressInput1, + address: [addressInput1], fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: addressInput1.city }, + address: [{ city: addressInput1.city }], }), }, { id: userInput2.id, - address: addressInput2, + address: [addressInput2], fullName: fullNameResolver({ firstName: userInput2.firstName, lastName: userInput2.lastName, - address: { city: addressInput2.city }, + address: [{ city: addressInput2.city }], }), }, ]), @@ -628,12 +628,13 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName address { city } lastName") } `; - const fullNameResolver = ({ firstName, lastName, address }) => `${firstName} ${lastName} from ${address.city}`; + const fullNameResolver = ({ firstName, lastName, address }) => + `${firstName} ${lastName} from ${address[0].city}`; const resolvers = { [User.name]: { @@ -666,20 +667,20 @@ describe("Related Fields", () => { [User.plural]: expect.toIncludeSameMembers([ { id: userInput1.id, - address: addressInput1, + address: [addressInput1], fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: addressInput1.city }, + address: [{ city: addressInput1.city }], }), }, { id: userInput2.id, - address: addressInput2, + address: [addressInput2], fullName: fullNameResolver({ firstName: userInput2.firstName, lastName: userInput2.lastName, - address: { city: addressInput2.city }, + address: [{ city: addressInput2.city }], }), }, ]), @@ -705,23 +706,23 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; const fullNameResolver = ({ firstName, lastName, address }) => { - if (address.city.population) { - return `${firstName} ${lastName} from ${address.city.name} with population of ${address.city.population}`; + if (address[0].city[0].population) { + return `${firstName} ${lastName} from ${address[0].city[0].name} with population of ${address[0].city[0].population}`; } - return `${firstName} ${lastName} from ${address.city.name}`; + return `${firstName} ${lastName} from ${address[0].city[0].name}`; }; const resolvers = { @@ -752,23 +753,27 @@ describe("Related Fields", () => { expect(result.data as any).toEqual({ [User.plural]: expect.toIncludeSameMembers([ { - address: { - street: addressInput1.street, - }, + address: [ + { + street: addressInput1.street, + }, + ], fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: cityInput1 }, + address: [{ city: [cityInput1] }], }), }, { - address: { - street: addressInput2.street, - }, + address: [ + { + street: addressInput2.street, + }, + ], fullName: fullNameResolver({ firstName: userInput2.firstName, lastName: userInput2.lastName, - address: { city: cityInput2 }, + address: [{ city: [cityInput2] }], }), }, ]), @@ -796,28 +801,28 @@ describe("Related Fields", () => { type ${City} @node { name: String! population: Int - state: ${State}! @relationship(type: "IN_STATE", direction: OUT) + state: [${State}!]! @relationship(type: "IN_STATE", direction: OUT) } type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city:[${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name state { someValue } population } }") } `; const fullNameResolver = ({ firstName, lastName, address }) => { - if (address.city.population) { - return `${firstName} ${lastName} from ${address.city.name} with population of ${address.city.population} with ${address.city.state.someValue}`; + if (address[0].city[0].population) { + return `${firstName} ${lastName} from ${address[0].city[0].name} with population of ${address[0].city[0].population} with ${address[0].city[0].state[0].someValue}`; } - return `${firstName} ${lastName} from ${address.city.name}`; + return `${firstName} ${lastName} from ${address[0].city[0].name}`; }; const resolvers = { @@ -848,35 +853,47 @@ describe("Related Fields", () => { expect(result.data as any).toEqual({ [User.plural]: expect.toIncludeSameMembers([ { - address: { - street: addressInput1.street, - }, + address: [ + { + street: addressInput1.street, + }, + ], fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { - city: { - name: cityInput1.name, - population: cityInput1.population, - state: stateInput, + address: [ + { + city: [ + { + name: cityInput1.name, + population: cityInput1.population, + state: [stateInput], + }, + ], }, - }, + ], }), }, { - address: { - street: addressInput2.street, - }, + address: [ + { + street: addressInput2.street, + }, + ], fullName: fullNameResolver({ firstName: userInput2.firstName, lastName: userInput2.lastName, - address: { - city: { - name: cityInput2.name, - population: cityInput2.population, - state: stateInput, + address: [ + { + city: [ + { + name: cityInput2.name, + population: cityInput2.population, + state: [stateInput], + }, + ], }, - }, + ], }), }, ]), @@ -907,12 +924,12 @@ describe("Related Fields", () => { type ${Book} @node { title: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } type ${Journal} @node { subject: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } `; @@ -989,23 +1006,23 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! @alias(property: "first") lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; const fullNameResolver = ({ firstName, lastName, address }) => { - if (address.city.population) { - return `${firstName} ${lastName} from ${address.city.name} with population of ${address.city.population}`; + if (address[0].city[0].population) { + return `${firstName} ${lastName} from ${address[0].city[0].name} with population of ${address[0].city[0].population}`; } - return `${firstName} ${lastName} from ${address.city.name}`; + return `${firstName} ${lastName} from ${address[0].city[0].name}`; }; const resolvers = { @@ -1037,25 +1054,29 @@ describe("Related Fields", () => { expect(result.data as any).toEqual({ [User.plural]: expect.toIncludeSameMembers([ { - address: { - street: addressInput1.street, - }, + address: [ + { + street: addressInput1.street, + }, + ], firstName: userInput1.firstName, fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: cityInput1 }, + address: [{ city: [cityInput1] }], }), }, { - address: { - street: addressInput2.street, - }, + address: [ + { + street: addressInput2.street, + }, + ], firstName: userInput2.firstName, fullName: fullNameResolver({ firstName: userInput2.firstName, lastName: userInput2.lastName, - address: { city: cityInput2 }, + address: [{ city: [cityInput2] }], }), }, ]), @@ -1092,14 +1113,14 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; @@ -1170,14 +1191,14 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; @@ -1250,14 +1271,14 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; @@ -1330,23 +1351,23 @@ describe("Related Fields", () => { type ${Address} @node { street: String! - city: ${City}! @relationship(type: "IN_CITY", direction: OUT) + city: [${City}!]! @relationship(type: "IN_CITY", direction: OUT) } type ${User} @node { id: ID! firstName: String! @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { city { name population } }") } `; const fullNameResolver = ({ firstName, lastName, address }) => { - if (address.city.population) { - return `${firstName} ${lastName} from ${address.city.name} with population of ${address.city.population}`; + if (address[0].city[0].population) { + return `${firstName} ${lastName} from ${address[0].city[0].name} with population of ${address[0].city[0].population}`; } - return `${firstName} ${lastName} from ${address.city.name}`; + return `${firstName} ${lastName} from ${address[0].city[0].name}`; }; const resolvers = { @@ -1381,14 +1402,16 @@ describe("Related Fields", () => { expect(result.data as any).toEqual({ [User.plural]: expect.toIncludeSameMembers([ { - address: { - street: addressInput1.street, - }, + address: [ + { + street: addressInput1.street, + }, + ], firstName: userInput1.firstName, fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { city: cityInput1 }, + address: [{ city: [cityInput1] }], }), }, ]), @@ -1610,12 +1633,12 @@ describe("Related Fields", () => { type ${Book} @node { title: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } type ${Journal} @node { subject: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } `; @@ -1704,12 +1727,12 @@ describe("Related Fields", () => { type ${Book} @node { title: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } type ${Journal} @node { subject: String! - author: ${Author}! @relationship(type: "WROTE", direction: IN) + author: [${Author}!]! @relationship(type: "WROTE", direction: IN) } `; @@ -2133,13 +2156,13 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { houseNumber street }") } `; const fullNameResolver = ({ firstName, lastName, address }) => - `${firstName} ${lastName} from ${address.houseNumber} ${address.street} ${address.city}`; + `${firstName} ${lastName} from ${address[0].houseNumber} ${address[0].street} ${address[0].city}`; const resolvers = { [User.name]: { @@ -2169,7 +2192,7 @@ describe("Related Fields", () => { fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { street: addressInput1.street, houseNumber: 12 }, + address: [{ street: addressInput1.street, houseNumber: 12 }], }), }, ], @@ -2193,13 +2216,13 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { houseNumber street }") } `; const fullNameResolver = ({ firstName, lastName, address }) => - `${firstName} ${lastName} from ${address.houseNumber} ${address.street}`; + `${firstName} ${lastName} from ${address[0].houseNumber} ${address[0].street}`; const resolvers = { [User.name]: { @@ -2229,7 +2252,7 @@ describe("Related Fields", () => { fullName: fullNameResolver({ firstName: userInput1.firstName, lastName: userInput1.lastName, - address: { street: addressInput1.street, houseNumber: 12 }, + address: [{ street: addressInput1.street, houseNumber: 12 }], }), }, ], @@ -2248,7 +2271,7 @@ describe("Related Fields", () => { id: ID! firstName: String! lastName: String! - address: ${Address} @relationship(type: "LIVES_AT", direction: OUT) + address: [${Address}!]! @relationship(type: "LIVES_AT", direction: OUT) fullName: String @customResolver(requires: "firstName lastName address { houseNumber street }") } diff --git a/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts index c5b2dc3d5e..eb872a55c3 100644 --- a/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/cypher-auth.int.test.ts @@ -37,21 +37,21 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { const typeDefs = /* GraphQL */ ` type ${User} @node(labels: ["${User}"]) @authorization( filter: [ - { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, + { where: { node: { NOT: { blockedUsers_SOME: { to_SINGLE: { id_EQ: "$jwt.sub" } } } } } }, ] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } type ${UserBlockedUser} @node(labels: ["${UserBlockedUser}"]) @authorization( filter: [ - { where: { node: { from: { id_EQ: "$jwt.sub" } } } } + { where: { node: { from_SINGLE: { id_EQ: "$jwt.sub" } } } } ] ) { - id: ID! @id @unique - from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) - to: ${User}! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) + id: ID! @id + from: [${User}!]! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) + to: [${User}!]! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) } type ${Thing} @node { diff --git a/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts b/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts index 330142981d..777a31af14 100644 --- a/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/cypher.int.test.ts @@ -49,7 +49,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -74,8 +74,8 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - query($title: String!) { + const source = /* GraphQL */ ` + query ($title: String!) { customMovies(title: $title) { title actors { @@ -114,7 +114,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -137,11 +137,11 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - query($title: String!, $name: String) { + const source = /* GraphQL */ ` + query ($title: String!, $name: String) { customMovies(title: $title) { title - actors(where: {name_EQ: $name}) { + actors(where: { name_EQ: $name }) { name } } @@ -177,7 +177,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWT @jwt { roles: [String!]! } @@ -187,7 +187,7 @@ describe("cypher directive", () => { actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } - type ${Actor} @authorization(validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES:"admin" } } }]) { + type ${Actor} @node @authorization(validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES:"admin" } } }]) { name: String! movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -208,11 +208,11 @@ describe("cypher directive", () => { features: { authorization: { key: "secret" } }, }); - const source = ` - query($title: String!, $name: String) { + const source = /* GraphQL */ ` + query ($title: String!, $name: String) { customMovies(title: $title) { title - actors(where: {name_EQ: $name}) { + actors(where: { name_EQ: $name }) { name } } @@ -253,7 +253,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -277,8 +277,8 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - query($titles: [String!]!) { + const source = /* GraphQL */ ` + query ($titles: [String!]!) { customMovies(titles: $titles) { title actors { @@ -335,7 +335,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -367,8 +367,8 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - query($title: String!) { + const source = /* GraphQL */ ` + query ($title: String!) { movie(title: $title) { title actorsConnection { @@ -436,7 +436,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -461,8 +461,8 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - mutation($title: String!) { + const source = /* GraphQL */ ` + mutation ($title: String!) { customMovies(title: $title) { title actors { @@ -501,7 +501,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) @@ -523,11 +523,11 @@ describe("cypher directive", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); - const source = ` - mutation($title: String!, $name: String) { + const source = /* GraphQL */ ` + mutation ($title: String!, $name: String) { customMovies(title: $title) { title - actors(where: {name_EQ: $name}) { + actors(where: { name_EQ: $name }) { name } } @@ -563,7 +563,7 @@ describe("cypher directive", () => { charset: "alphabetic", }); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWT @jwt { roles: [String!]! } @@ -573,7 +573,7 @@ describe("cypher directive", () => { actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } - type ${Actor} @authorization(validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) { + type ${Actor} @node @authorization(validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) { name: String! movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -592,11 +592,11 @@ describe("cypher directive", () => { features: { authorization: { key: "secret" } }, }); - const source = ` - mutation($title: String!, $name: String) { + const source = /* GraphQL */ ` + mutation ($title: String!, $name: String) { customMovies(title: $title) { title - actors(where: {name_EQ: $name}) { + actors(where: { name_EQ: $name }) { name } } @@ -637,7 +637,7 @@ describe("cypher directive", () => { Destination = testHelper.createUniqueType("Destination"); Town = testHelper.createUniqueType("Town"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Destination} @node { id: ID! preposition(caseName: String = null): String! @cypher(statement: "RETURN coalesce($caseName, '${defaultPreposition}') as result", columnName: "result") @@ -670,13 +670,13 @@ describe("cypher directive", () => { }); test("should return default value", async () => { - const source = ` - query($id: ID!) { - townDestinationList(id: $id) { - id - preposition - } + const source = /* GraphQL */ ` + query ($id: ID!) { + townDestinationList(id: $id) { + id + preposition } + } `; const expectedTownDestinationList = [{ id: destinationId, preposition: defaultPreposition }]; @@ -694,14 +694,14 @@ describe("cypher directive", () => { }); test("should return test value", async () => { - const source = ` - query($id: ID!, $caseName: String) { - townDestinationList(id: $id) { - id - preposition(caseName: $caseName) - } + const source = /* GraphQL */ ` + query ($id: ID!, $caseName: String) { + townDestinationList(id: $id) { + id + preposition(caseName: $caseName) } - `; + } + `; const expectedTownDestinationList = [{ id: destinationId, preposition: testCaseName }]; @@ -731,7 +731,7 @@ describe("cypher directive", () => { Destination = testHelper.createUniqueType("Destination"); Town = testHelper.createUniqueType("Town"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Destination} @node { id: ID! preposition(caseName: String): String! @cypher(statement: "RETURN coalesce($caseName, '${defaultPreposition}') as result", columnName: "result") @@ -764,13 +764,13 @@ describe("cypher directive", () => { }); test("should return default value", async () => { - const source = ` - query($id: ID!) { - townDestinationList(id: $id) { - id - preposition - } + const source = /* GraphQL */ ` + query ($id: ID!) { + townDestinationList(id: $id) { + id + preposition } + } `; const expectedTownDestinationList = [{ id: destinationId, preposition: defaultPreposition }]; @@ -788,14 +788,14 @@ describe("cypher directive", () => { }); test("should return test value", async () => { - const source = ` - query($id: ID!, $caseName: String) { - townDestinationList(id: $id) { - id - preposition(caseName: $caseName) - } + const source = /* GraphQL */ ` + query ($id: ID!, $caseName: String) { + townDestinationList(id: $id) { + id + preposition(caseName: $caseName) } - `; + } + `; const expectedTownDestinationList = [{ id: destinationId, preposition: testCaseName }]; @@ -826,7 +826,7 @@ describe("cypher directive", () => { Movie = new UniqueType("Movie"); User = new UniqueType("User"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` union PostMovieUser = ${Post} | ${Movie} | ${User} type ${Post} @node { @@ -838,7 +838,7 @@ describe("cypher directive", () => { } type ${User} @node { - id: ID @id @unique + id: ID @id updates: [PostMovieUser!]! @cypher( statement: """ @@ -868,7 +868,7 @@ describe("cypher directive", () => { }); test("should return __typename", async () => { - const source = ` + const source = /* GraphQL */ ` query { ${User.plural} (where: { id_EQ: "${userId}" }) { updates { diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts index fd216d9ac4..8061816261 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-aggregation.test.ts @@ -236,12 +236,12 @@ describe("cypher directive filtering - Aggregation", () => { }); }); - test("String aggregation - min, filter on [Int]", async () => { + test("String aggregation - min, filter on [Int!]", async () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String released: Int - custom_field: [Int] + custom_field: [Int!] @cypher( statement: """ MATCH (this) @@ -287,12 +287,12 @@ describe("cypher directive filtering - Aggregation", () => { }); }); - test("String aggregation - min, filter on [String]", async () => { + test("String aggregation - min, filter on [String!]", async () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String released: Int - custom_field: [String] + custom_field: [String!] @cypher( statement: """ MATCH (this) diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-auth.int.test.ts index 835340b847..e6411d2ee9 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-auth.int.test.ts @@ -32,7 +32,7 @@ describe("cypher directive filtering - Auth", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) { + type ${Movie} @node @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) { title: String custom_field: String @cypher( @@ -110,7 +110,7 @@ describe("cypher directive filtering - Auth", () => { """ columnName: "s" ) - @authorization(filter: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } @@ -177,7 +177,7 @@ describe("cypher directive filtering - Auth", () => { """ columnName: "s" ) - @authorization(filter: [{ where: { node: { title: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { title: { eq: "$jwt.custom_value" } } } }]) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } @@ -209,7 +209,7 @@ describe("cypher directive filtering - Auth", () => { {} ); - const query = ` + const query = /* GraphQL */ ` query { ${Movie.plural} { title @@ -250,7 +250,7 @@ describe("cypher directive filtering - Auth", () => { actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } - type ${Actor} @node @authorization(filter: [{ where: { node: { movies_SOME: { custom_field: "$jwt.custom_value" } } } }]) { + type ${Actor} @node @authorization(filter: [{ where: { node: { movies: { some: { custom_field: { eq: "$jwt.custom_value" }} } } } }]) { name: String movies: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -280,7 +280,7 @@ describe("cypher directive filtering - Auth", () => { {} ); - const query = ` + const query = /* GraphQL */ ` query { ${Actor.plural} { name @@ -306,7 +306,7 @@ describe("cypher directive filtering - Auth", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { - title: String @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) + title: String @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) custom_field: String @cypher( statement: """ @@ -346,7 +346,7 @@ describe("cypher directive filtering - Auth", () => { {} ); - const query = ` + const query = /* GraphQL */ ` query { ${Movie.plural} { title @@ -374,7 +374,7 @@ describe("cypher directive filtering - Auth", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) { title: String custom_field: String @cypher( @@ -434,7 +434,7 @@ describe("cypher directive filtering - Auth", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) { title: String custom_field: String @cypher( diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts index 94138cacdb..654de5ecf4 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list-auth.int.test.ts @@ -34,7 +34,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node @authorization(filter: [{ where: { node: { custom_field_INCLUDES: "$jwt.custom_value" } } }]) { title: String - custom_field: [String] + custom_field: [String!] @cypher( statement: """ MATCH (this) @@ -187,7 +187,7 @@ describe("cypher directive filtering - List Auth", () => { """ columnName: "list" ) - @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_field_EQ: "$jwt.custom_value" } } }]) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) } diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts index 1f717dc346..98d0f0d410 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-list.int.test.ts @@ -53,7 +53,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "a" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "a" } }) { title } } @@ -92,7 +92,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: 2 }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: 2 }}) { title } } @@ -131,7 +131,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: 20.0 }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: 20.0 } }) { title } } @@ -153,7 +153,7 @@ describe("cypher directive filtering - List", () => { const typeDefs = /* GraphQL */ ` type ${CustomType} @node { title: String - custom_cypher_list: [Point] @cypher(statement: + custom_cypher_list: [Point!] @cypher(statement: """ MATCH (this) RETURN this.custom_data as list @@ -170,7 +170,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: { latitude: 1, longitude: 2 } }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: { latitude: 1, longitude: 2 } } }) { title } } @@ -209,7 +209,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: { x: 1, y: 2, z: 3 } }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: { x: 1, y: 2, z: 3 } } }) { title } } @@ -248,7 +248,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01T00:00:00Z" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "2021-01-01T00:00:00Z" } }) { title } } @@ -287,7 +287,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01T00:00:00" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "2021-01-01T00:00:00" } }) { title } } @@ -326,7 +326,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "2021-01-01" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "2021-01-01"} }) { title } } @@ -365,7 +365,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "12:00:00" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "12:00:00" }}) { title } } @@ -404,7 +404,7 @@ describe("cypher directive filtering - List", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { custom_cypher_list_INCLUDES: "12:00:00" }) { + ${CustomType.plural}(where: { custom_cypher_list: { includes: "12:00:00" } }) { title } } diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.int.test.ts index 75307c3762..b08eabb722 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.int.test.ts @@ -75,7 +75,7 @@ describe("cypher directive filtering - One To One Relationship", () => { ${Movie.plural}( where: { actor: { - name_EQ: "Keanu Reeves" + name: { eq: "Keanu Reeves" } } } ) { @@ -163,7 +163,7 @@ describe("cypher directive filtering - One To One Relationship", () => { ${Actor.plural}( where: { movie: { - title_STARTS_WITH: "The Matrix" + title: { startsWith: "The Matrix" } } } ) { @@ -191,6 +191,7 @@ describe("cypher directive filtering - One To One Relationship", () => { }); }); + // TODO: {actor: null} was not migrated to {actors: {eq: null}}. Check if this is correct test("1 to 1 relationship with null filter", async () => { const Movie = testHelper.createUniqueType("Movie"); const Actor = testHelper.createUniqueType("Actor"); @@ -239,7 +240,7 @@ describe("cypher directive filtering - One To One Relationship", () => { query { ${Movie.plural}( where: { - released_EQ: 2003, + released: {eq: 2003}, actor: null } ) { @@ -260,6 +261,7 @@ describe("cypher directive filtering - One To One Relationship", () => { }); }); + // TODO: {actor: null} was not migrated to {actors: {eq: null}}. Check if this is correct test("1 to 1 relationship with NOT null filter", async () => { const Movie = testHelper.createUniqueType("Movie"); const Actor = testHelper.createUniqueType("Actor"); @@ -309,7 +311,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { ${Movie.plural}( - where: { AND: [{ released_IN: [2003], NOT: { actor: null } }] } + where: { AND: [{ released: { in: [2003] }, NOT: { actor: null } }] } ) { title } @@ -336,7 +338,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const Person = testHelper.createUniqueType("Person"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) { title: String released: Int directed_by: ${Person}! @@ -387,7 +389,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" }} }) { directed { title directed_by { @@ -420,7 +422,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const Person = testHelper.createUniqueType("Person"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(filter: [{ where: { node: { directed_by: { name:{ eq: "$jwt.custom_value" }} } } }]) { title: String released: Int directed_by: ${Person}! @@ -471,7 +473,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -495,7 +497,7 @@ describe("cypher directive filtering - One To One Relationship", () => { type ${Movie} @node { title: String released: Int - directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:${Person}) @@ -543,7 +545,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -579,7 +581,7 @@ describe("cypher directive filtering - One To One Relationship", () => { type ${Movie} @node { title: String released: Int - directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + directed_by: ${Person}! @authorization(filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:${Person}) @@ -627,7 +629,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -648,7 +650,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const Person = testHelper.createUniqueType("Person"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) { title: String released: Int directed_by: ${Person}! @@ -699,7 +701,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -732,7 +734,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const Person = testHelper.createUniqueType("Person"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) { title: String released: Int directed_by: ${Person}! @@ -783,7 +785,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -808,7 +810,7 @@ describe("cypher directive filtering - One To One Relationship", () => { type ${Movie} @node { title: String released: Int - directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name: "$jwt.custom_value" } } } }]) + directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:${Person}) @@ -856,7 +858,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -892,7 +894,7 @@ describe("cypher directive filtering - One To One Relationship", () => { type ${Movie} @node { title: String released: Int - directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + directed_by: ${Person}! @authorization(validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:${Person}) @@ -940,7 +942,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1015,7 +1017,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Person.plural}(where: { directed: { title_EQ: "The Matrix" } }) { + ${Person.plural}(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1133,7 +1135,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { directed_by: { name_EQ: "Lilly Wachowski"}, title_ENDS_WITH: "Matrix" }) { + ${Movie.plural}(where: { directed_by: { name: { eq: "Lilly Wachowski"}}, title: { endsWith: "Matrix" }}) { actorsConnection { totalCount edges { diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-scalar.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-scalar.int.test.ts index ab5e95a59f..b986b3c1db 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-scalar.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-scalar.int.test.ts @@ -36,27 +36,27 @@ describe("cypher directive filtering - Scalar", () => { test.each([ { title: "Int cypher field: exact match", - filter: `special_count_EQ: 1`, + filter: `special_count: { eq: 1 }`, }, { title: "Int cypher field: GT", - filter: `special_count_GT: 0`, + filter: `special_count: { gt: 0 }`, }, { title: "Int cypher field: GTE", - filter: `special_count_GTE: 1`, + filter: `special_count: { gte: 1 }`, }, { title: "Int cypher field: LT", - filter: `special_count_LT: 2`, + filter: `special_count: { lt: 2 }`, }, { title: "Int cypher field: LTE", - filter: `special_count_LTE: 2`, + filter: `special_count: { lte: 2 }`, }, { title: "Int cypher field: IN", - filter: `special_count_IN: [1, 2, 3]`, + filter: `special_count: { in: [1, 2, 3]}`, }, ] as const)("$title", async ({ filter }) => { const typeDefs = /* GraphQL */ ` @@ -85,7 +85,6 @@ describe("cypher directive filtering - Scalar", () => { `; const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeFalsy(); expect(gqlResult?.data).toEqual({ [CustomType.plural]: [ @@ -99,23 +98,23 @@ describe("cypher directive filtering - Scalar", () => { test.each([ { title: "String cypher field: exact match", - filter: `special_word_EQ: "test"`, + filter: `special_word: { eq: "test"}`, }, { title: "String cypher field: CONTAINS", - filter: `special_word_CONTAINS: "es"`, + filter: `special_word: { contains: "es"}`, }, { title: "String cypher field: ENDS_WITH", - filter: `special_word_ENDS_WITH: "est"`, + filter: `special_word:{ endsWith: "est"}`, }, { title: "String cypher field: STARTS_WITH", - filter: `special_word_STARTS_WITH: "tes"`, + filter: `special_word:{ startsWith: "tes"}`, }, { title: "String cypher field: IN", - filter: `special_word_IN: ["test", "test2"]`, + filter: `special_word:{ in: ["test", "test2"]}`, }, ] as const)("$title", async ({ filter }) => { const typeDefs = /* GraphQL */ ` @@ -185,7 +184,7 @@ describe("cypher directive filtering - Scalar", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { special_count_GTE: 1, title_EQ: "CustomType One" }) { + ${CustomType.plural}(where: { special_count: { gte: 1 }, title: { eq: "CustomType One"} }) { special_count } } @@ -234,7 +233,7 @@ describe("cypher directive filtering - Scalar", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { special_count_GTE: 1, title_EQ: "CustomType Unknown" }) { + ${CustomType.plural}(where: { special_count: { gt: 1 }, title: { eq: "CustomType Unknown"} }) { special_count } } @@ -268,7 +267,7 @@ describe("cypher directive filtering - Scalar", () => { const query = /* GraphQL */ ` query { - ${CustomType.plural}(where: { special_count_GTE: 1 }) { + ${CustomType.plural}(where: { special_count: { gte: 1 }}) { title } } diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-sorting.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-sorting.int.test.ts index c8ee572a5b..e501ecb813 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-sorting.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-sorting.int.test.ts @@ -73,7 +73,7 @@ describe("cypher directive filtering - Sorting", () => { const query = /* GraphQL */ ` query { ${Movie.plural}( - where: { custom_field_STARTS_WITH: "The Matrix" } + where: { custom_field: { startsWith: "The Matrix" }} sort: [{ custom_field: DESC }] ) { title @@ -159,7 +159,7 @@ describe("cypher directive filtering - Sorting", () => { const query = /* GraphQL */ ` query { ${Movie.plural}( - where: { custom_field_EQ: "hello world!" } + where: { custom_field: { eq: "hello world!" } } sort: [{ title: DESC }] ) { title diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-temporal.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-temporal.int.test.ts index e79e10a7b6..35dac2afef 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-temporal.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/cypher-filtering-temporal.int.test.ts @@ -62,7 +62,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_time_GT: "2024-09-02T00:00:00Z" + special_time: { gt: "2024-09-02T00:00:00Z"} } ) { special_time @@ -116,7 +116,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_duration_EQ: "P14DT16H12M" + special_duration: { eq: "P14DT16H12M" } } ) { title @@ -165,7 +165,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_duration_LT: "P14DT16H13M" + special_duration: { lt: "P14DT16H13M" } } ) { title @@ -217,7 +217,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_duration_LTE: "P14DT16H12M" + special_duration: { lte: "P14DT16H12M"} } ) { title @@ -269,7 +269,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_duration_GT: "P14DT16H11M" + special_duration: { gt: "P14DT16H11M"} } ) { title @@ -321,7 +321,7 @@ describe("cypher directive filtering - Temporal", () => { query { ${CustomType.plural}( where: { - special_duration_GTE: "P14DT16H12M" + special_duration: { gte: "P14DT16H12M"} } ) { title diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.int.test.ts index ffd48be8b8..8dd4b53ab0 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.int.test.ts @@ -31,7 +31,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(filter: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }]) { title: String rating: Float actors: [${Actor}!]! @@ -91,7 +91,7 @@ describe("cypher directive filtering - relationship auth filter", () => { query { ${Movie.operations.connection}( where: { - rating_GT: 7.0 + rating: { gt: 7.0 } } ) { edges { @@ -124,7 +124,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(filter: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }]) { title: String rating: Float actors: [${Actor}!]! @@ -184,7 +184,7 @@ describe("cypher directive filtering - relationship auth filter", () => { query { ${Movie.operations.connection}( where: { - rating_GT: 7.0 + rating:{ gt: 7.0} } ) { edges { @@ -211,7 +211,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }]) { title: String rating: Float actors: [${Actor}!]! @@ -271,7 +271,7 @@ describe("cypher directive filtering - relationship auth filter", () => { query { ${Movie.operations.connection}( where: { - rating_LT: 7.0 + rating: { lt: 7.0 } } ) { edges { @@ -304,7 +304,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const Actor = testHelper.createUniqueType("Actor"); const typeDefs = /* GraphQL */ ` - type ${Movie} @node @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + type ${Movie} @node @authorization(validate: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }]) { title: String rating: Float actors: [${Actor}!]! @@ -364,7 +364,7 @@ describe("cypher directive filtering - relationship auth filter", () => { query { ${Movie.operations.connection}( where: { - rating_GT: 7.0 + rating: { gt: 7.0 } } ) { edges { diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.int.test.ts index 348c1bc666..e1a2a399eb 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.int.test.ts @@ -82,8 +82,12 @@ describe("Connection API - cypher directive filtering - Relationship", () => { ${Movie.operations.connection}( where: { NOT: { - actors_SOME: { - name_EQ: "Jada Pinkett Smith" + actors: { + some: { + name: { + eq: "Jada Pinkett Smith" + } + } } } } @@ -159,8 +163,12 @@ describe("Connection API - cypher directive filtering - Relationship", () => { query { ${Movie.operations.connection}( where: { - actors_ALL: { - name_EQ: "Keanu Reeves" + actors: { + all: { + name: { + eq: "Keanu Reeves" + } + } } } ) { @@ -239,8 +247,12 @@ describe("Connection API - cypher directive filtering - Relationship", () => { query { ${Movie.operations.connection}( where: { - actors_SINGLE: { - name_EQ: "Carrie-Anne Moss" + actors: { + single: { + name: { + eq: "Carrie-Anne Moss" + } + } } } ) { @@ -317,8 +329,10 @@ describe("Connection API - cypher directive filtering - Relationship", () => { query { ${Movie.operations.connection}( where: { - actors_SOME: { - name_EQ: "Keanu Reeves" + actors: { + some: { + name: { eq: "Keanu Reeves" } + } } } ) { @@ -400,8 +414,10 @@ describe("Connection API - cypher directive filtering - Relationship", () => { query { ${Movie.operations.connection}( where: { - actors_SOME: { - name_EQ: "Keanu Reeves" + actors: { + some: { + name: { eq: "Keanu Reeves" } + } } } sort: { @@ -485,8 +501,10 @@ describe("Connection API - cypher directive filtering - Relationship", () => { query { ${Movie.operations.connection}( where: { - actors_NONE: { - name_EQ: "Keanu Reeves" + actors: { + none: { + name: { eq: "Keanu Reeves" } + } } } ) { @@ -599,8 +617,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { ${Movie.operations.connection}( where: { OR: [ - { actors_SOME: { name_EQ: "Jada Pinkett Smith" } }, - { genres_SOME: { name_EQ: "Romance" } } + { actors: { some: { name: { eq: "Jada Pinkett Smith" } } }}, + { genres: { some: { name: { eq: "Romance" }} } } ] } ) diff --git a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship.int.test.ts b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship.int.test.ts index 471ff4f4eb..0b83ac8e2a 100644 --- a/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship.int.test.ts +++ b/packages/graphql/tests/integration/directives/cypher/filtering/relationships/cypher-filtering-relationship.int.test.ts @@ -82,8 +82,10 @@ describe("cypher directive filtering - Relationship", () => { ${Movie.plural}( where: { NOT: { - actors_SOME: { - name_EQ: "Jada Pinkett Smith" + actors: { + some: { + name: { eq: "Jada Pinkett Smith"} + } } } } @@ -153,8 +155,8 @@ describe("cypher directive filtering - Relationship", () => { query { ${Movie.plural}( where: { - actors_ALL: { - name_EQ: "Keanu Reeves" + actors: { + all: { name: { eq: "Keanu Reeves" } } } } ) { @@ -225,8 +227,10 @@ describe("cypher directive filtering - Relationship", () => { query { ${Movie.plural}( where: { - actors_SINGLE: { - name_EQ: "Carrie-Anne Moss" + actors: { + single: { + name: { eq: "Carrie-Anne Moss" } + } } } ) { @@ -295,8 +299,10 @@ describe("cypher directive filtering - Relationship", () => { query { ${Movie.plural}( where: { - actors_SOME: { - name_EQ: "Keanu Reeves" + actors: { + some: { + name: { eq: "Keanu Reeves" } + } } } ) { @@ -367,8 +373,10 @@ describe("cypher directive filtering - Relationship", () => { query { ${Movie.plural}( where: { - actors_NONE: { - name_EQ: "Keanu Reeves" + actors: { + none: { + name: { eq: "Keanu Reeves" } + } } } ) { @@ -473,8 +481,8 @@ describe("cypher directive filtering - Relationship", () => { ${Movie.plural}( where: { OR: [ - { actors_SOME: { name_EQ: "Jada Pinkett Smith" } }, - { genres_SOME: { name_EQ: "Romance" } } + { actors: { some: { name: { eq: "Jada Pinkett Smith" } } } }, + { genres: { some: { name: { eq: "Romance" } } } } ] } ) diff --git a/packages/graphql/tests/integration/directives/fulltext/fulltext-argument.int.test.ts b/packages/graphql/tests/integration/directives/fulltext/fulltext-argument.int.test.ts index ddac17d3d3..1a9842d4e7 100644 --- a/packages/graphql/tests/integration/directives/fulltext/fulltext-argument.int.test.ts +++ b/packages/graphql/tests/integration/directives/fulltext/fulltext-argument.int.test.ts @@ -76,7 +76,7 @@ describe("@fulltext directive", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ name: "${indexName}", fields: ["title"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitle", fields: ["title"] }]) @node { title: String! } `; @@ -103,7 +103,7 @@ describe("@fulltext directive", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ name: "${indexName}", fields: ["title", "description"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) @node { title: String! description: String! } @@ -136,7 +136,7 @@ describe("@fulltext directive", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ name: "${indexName}", fields: ["title", "description"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) @node { title: String! description: String! @alias(property: "${alias}") } diff --git a/packages/graphql/tests/integration/directives/fulltext/fulltext-index.int.test.ts b/packages/graphql/tests/integration/directives/fulltext/fulltext-index.int.test.ts index da634668ac..9d03099201 100644 --- a/packages/graphql/tests/integration/directives/fulltext/fulltext-index.int.test.ts +++ b/packages/graphql/tests/integration/directives/fulltext/fulltext-index.int.test.ts @@ -75,7 +75,7 @@ describe("@fulltext directive - indexes constraints", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", fields: ["title"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitle", fields: ["title"] }]) @node { title: String! } `; @@ -101,7 +101,7 @@ describe("@fulltext directive - indexes constraints", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", fields: ["title", "description"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) @node { title: String! description: String! } @@ -135,7 +135,7 @@ describe("@fulltext directive - indexes constraints", () => { const type = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", fields: ["title", "description"] }]) @node { + type ${type.name} @fulltext(indexes: [{ indexName: "${indexName}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) @node { title: String! description: String! @alias(property: "${alias}") } diff --git a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts index 107c2caa8f..309c0550ca 100644 --- a/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts +++ b/packages/graphql/tests/integration/directives/fulltext/fulltext-query.int.test.ts @@ -17,7 +17,6 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import { type Driver } from "neo4j-driver"; import { generate } from "randomstring"; import type { Neo4jGraphQL } from "../../../../src/classes"; @@ -27,10 +26,11 @@ import { createBearerToken } from "../../../utils/create-bearer-token"; import type { UniqueType } from "../../../utils/graphql-types"; import { isMultiDbUnsupportedError } from "../../../utils/is-multi-db-unsupported-error"; import { TestHelper } from "../../../utils/tests-helper"; +import { GraphQLError } from "graphql"; function generatedTypeDefs(personType: UniqueType, movieType: UniqueType): string { return ` - type ${personType.name} @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) @node { + type ${personType.name} @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${personType.plural}ByName", fields: ["name"] }]) @node { name: String! born: Int! actedInMovies: [${movieType.name}!]! @relationship(type: "ACTED_IN", direction: OUT) @@ -91,7 +91,6 @@ describe("@fulltext directive", () => { let neoSchema: Neo4jGraphQL; let personType: UniqueType; let movieType: UniqueType; - let personTypeLowerFirst: string; let queryType: string; const person1 = { @@ -126,8 +125,7 @@ describe("@fulltext directive", () => { personType = testHelper.createUniqueType("Person"); movieType = testHelper.createUniqueType("Movie"); - queryType = `${personType.plural}Fulltext${upperFirst(personType.name)}Index`; - personTypeLowerFirst = personType.singular; + queryType = `${personType.plural}ByName`; const typeDefs = generatedTypeDefs(personType, movieType); @@ -166,33 +164,35 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ name: person2.name, }); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ name: person1.name, }); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[2].node).toEqual({ name: person3.name, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD] ); }); @@ -203,33 +203,35 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "some name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ name: person3.name, }); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ name: person1.name, }); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[2].node).toEqual({ name: person2.name, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD] ); }); @@ -240,20 +242,22 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "should not match") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([]); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([]); }); test("Filters node to single result", async () => { @@ -263,22 +267,24 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { name_EQ: "${person1.name}" } }) { - score - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a different name", where: { node: { name_EQ: "${person1.name}" } }) { + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); }); test("Filters node to multiple results", async () => { @@ -288,26 +294,28 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { born_GTE: ${person2.born} } }) { - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a different name", where: { node: { born_GTE: ${person2.born} } }) { + edges { + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: { + node: { name: person2.name, }, }, { - [personTypeLowerFirst]: { + node: { name: person3.name, }, }, @@ -321,13 +329,15 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { name_CONTAINS: "not in anything!!" } }) { - score - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a different name", where: { node: { name_CONTAINS: "not in anything!!" } }) { + edges { + score + node { + name + } + } } } `; @@ -335,7 +345,7 @@ describe("@fulltext directive", () => { const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([]); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([]); }); test("Filters score to single result", async () => { @@ -345,22 +355,24 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { min: 0.5 } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person2.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person2.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); }); test("Filters score to multiple results", async () => { @@ -370,25 +382,27 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { max: 0.5 } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst].name).toBe(person3.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[1].node.name).toBe(person3.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(2); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(2); }); test("Filters score to no results", async () => { @@ -398,20 +412,22 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { min: 100 } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([]); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([]); }); test("Filters score with combined min and max", async () => { @@ -421,22 +437,24 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { min: 0.201, max: 0.57 } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); }); test("Filters score with max score of 0", async () => { @@ -446,20 +464,22 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { max: 0 } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([]); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([]); }); test("Throws error if score filtered with a non-number", async () => { @@ -470,13 +490,15 @@ describe("@fulltext directive", () => { } const nonNumberScoreInput = "not a number"; - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", where: { score: { max: "${nonNumberScoreInput}" } }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -496,30 +518,32 @@ describe("@fulltext directive", () => { const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { actedInMovies_SOME: { title_EQ: "${movie1.title}" } } }) { - score - ${personTypeLowerFirst} { - name - actedInMovies( sort: [{ released: DESC }] ) { - title - released - } - } + ${queryType}(phrase: "a different name", where: { node: { actedInMovies_SOME: { title_EQ: "${movie1.title}" } } }) { + edges { + score + node { + name + actedInMovies( sort: [{ released: DESC }] ) { + title + released + } + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person2.name); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].actedInMovies).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person2.name); + expect((gqlResult.data?.[queryType] as any).edges[0].node.actedInMovies).toEqual([ { title: movie1.title, released: movie1.released, }, ]); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst].actedInMovies).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges[1].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[1].node.actedInMovies).toEqual([ { title: movie2.title, released: movie2.released, @@ -529,10 +553,10 @@ describe("@fulltext directive", () => { released: movie1.released, }, ]); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(2); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(2); }); test("Filters a related node to a single value", async () => { @@ -542,25 +566,27 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { actedInMovies_ALL: { released_EQ: ${movie1.released} } } }) { - ${personTypeLowerFirst} { - name - actedInMovies { - title - released - } - } + ${queryType}(phrase: "a different name", where: { node: { actedInMovies_ALL: { released_EQ: ${movie1.released} } } }) { + edges { + node { + name + actedInMovies { + title + released + } + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: { + node: { name: person2.name, actedInMovies: [ { @@ -582,22 +608,24 @@ describe("@fulltext directive", () => { const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { actedInMovies_ALL: { NOT: { released_IN: [${movie1.released}, ${movie2.released}] }} } }) { - score - ${personTypeLowerFirst} { - name - actedInMovies { - title - released - } - } + ${queryType}(phrase: "a different name", where: { node: { actedInMovies_ALL: { NOT: { released_IN: [${movie1.released}, ${movie2.released}] }} } }) { + edges { + score + node { + name + actedInMovies { + title + released + } + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([]); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([]); }); test("Throws an error for a non-string phrase", async () => { @@ -608,17 +636,19 @@ describe("@fulltext directive", () => { } const nonStringValue = '["not", "a", "string"]'; - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: ${nonStringValue}) { - score - ${personTypeLowerFirst} { - name - actedInMovies { - title - released - } - } + edges { + score + node { + name + actedInMovies { + title + released + } + } + } } } `; @@ -637,17 +667,19 @@ describe("@fulltext directive", () => { } const invalidField = "not_a_field"; - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "some name", where: { ${personTypeLowerFirst}: { ${invalidField}_EQ: "invalid" } }) { - score - ${personTypeLowerFirst} { - name - actedInMovies { - title - released - } - } + ${queryType}(phrase: "some name", where: { node: { ${invalidField}_EQ: "invalid" } }) { + edges { + score + node { + name + actedInMovies { + title + released + } + } + } } } `; @@ -665,27 +697,29 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name", sort: { score: ASC }) { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person3.name); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst].name).toBe(person2.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeLessThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person3.name); + expect((gqlResult.data?.[queryType] as any).edges[1].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[2].node.name).toBe(person2.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeLessThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeLessThanOrEqual( - (gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeLessThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD] ); }); @@ -696,25 +730,27 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", sort: [{ ${personTypeLowerFirst}: { name: ASC } }]) { - score - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a different name", sort: [{ node: { name: ASC } }]) { + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person3.name); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst].name).toBe(person2.name); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst].name).toBe(person1.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeNumber(); - expect((gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person3.name); + expect((gqlResult.data?.[queryType] as any).edges[1].node.name).toBe(person2.name); + expect((gqlResult.data?.[queryType] as any).edges[2].node.name).toBe(person1.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD]).toBeNumber(); }); test("Unordered sorting", async () => { @@ -724,25 +760,27 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "this is", sort: { ${personTypeLowerFirst}: { born: ASC, name: DESC } }) { - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "this is", sort: { node: { born: ASC, name: DESC } }) { + edges { + node { + name + born + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: person1, + node: person1, }, { - [personTypeLowerFirst]: person2, + node: person2, }, ]); }); @@ -773,23 +811,27 @@ describe("@fulltext directive", () => { { person1, person2 } ); - const query1 = ` + const query1 = /* GraphQL */ ` query { - ${queryType}(phrase: "b", sort: [{ ${personTypeLowerFirst}: { born: DESC } }, { ${personTypeLowerFirst}: { name: ASC } }]) { - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "b", sort: [{ node: { born: DESC } }, { node: { name: ASC } }]) { + edges { + node { + name + born + } + } } } `; - const query2 = ` + const query2 = /* GraphQL */ ` query { - ${queryType}(phrase: "b", sort: [{ ${personTypeLowerFirst}: { name: ASC } }, { ${personTypeLowerFirst}: { born: DESC } }]) { - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "b", sort: [{ node: { name: ASC } }, { node: { born: DESC } }]) { + edges { + node { + name + born + } + } } } `; @@ -798,14 +840,8 @@ describe("@fulltext directive", () => { expect(gqlResult1.errors).toBeFalsy(); expect(gqlResult2.errors).toBeFalsy(); - expect(gqlResult1.data?.[queryType]).toEqual([ - { [personTypeLowerFirst]: person2 }, - { [personTypeLowerFirst]: person1 }, - ]); - expect(gqlResult2.data?.[queryType]).toEqual([ - { [personTypeLowerFirst]: person1 }, - { [personTypeLowerFirst]: person2 }, - ]); + expect((gqlResult1.data?.[queryType] as any).edges).toEqual([{ node: person2 }, { node: person1 }]); + expect((gqlResult2.data?.[queryType] as any).edges).toEqual([{ node: person1 }, { node: person2 }]); }); test("Ordered sorting, with score", async () => { @@ -834,25 +870,29 @@ describe("@fulltext directive", () => { { person1, person2 } ); - const query1 = ` + const query1 = /* GraphQL */ ` query { - ${queryType}(phrase: "b d", sort: [{ score: DESC }, { ${personTypeLowerFirst}: { name: ASC } }]) { - score - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "b d", sort: [{ score: DESC }, { node: { name: ASC } }]) { + edges { + score + node { + name + born + } + } } } `; - const query2 = ` + const query2 = /* GraphQL */ ` query { - ${queryType}(phrase: "b d", sort: [{ ${personTypeLowerFirst}: { name: ASC } }, { score: DESC }]) { - score - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "b d", sort: [{ node: { name: ASC } }, { score: DESC }]) { + edges { + score + node { + name + born + } + } } } `; @@ -861,14 +901,17 @@ describe("@fulltext directive", () => { expect(gqlResult1.errors).toBeFalsy(); expect(gqlResult2.errors).toBeFalsy(); - expect((gqlResult1.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual(person2); - expect((gqlResult1.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect((gqlResult1.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual(person1); - expect((gqlResult1.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeNumber(); - expect((gqlResult2.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual(person1); - expect((gqlResult2.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect((gqlResult2.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual(person2); - expect((gqlResult2.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeNumber(); + expect((gqlResult1.data?.[queryType] as any).edges[0].node).toEqual(person2); + expect((gqlResult1.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult1.data?.[queryType] as any).edges[1].node).toEqual(person1); + expect((gqlResult1.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeNumber(); + expect((gqlResult1.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThan( + (gqlResult1.data?.[queryType] as any).edges[1][SCORE_FIELD] + ); + expect((gqlResult2.data?.[queryType] as any).edges[0].node).toEqual(person1); + expect((gqlResult2.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult2.data?.[queryType] as any).edges[1].node).toEqual(person2); + expect((gqlResult2.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeNumber(); }); test("Sort on nested field", async () => { @@ -881,22 +924,24 @@ describe("@fulltext directive", () => { const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - ${personTypeLowerFirst} { - name - actedInMovies(sort: [{ released: ASC }]) { - title - released - } - } + edges { + node { + name + actedInMovies(sort: [{ released: ASC }]) { + title + released + } + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: { + node: { name: person1.name, actedInMovies: [ { @@ -911,7 +956,7 @@ describe("@fulltext directive", () => { }, }, { - [personTypeLowerFirst]: { + node: { name: person2.name, actedInMovies: [ { @@ -922,7 +967,7 @@ describe("@fulltext directive", () => { }, }, { - [personTypeLowerFirst]: { + node: { name: person3.name, actedInMovies: [ { @@ -942,26 +987,28 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name", sort: { score: ASC }, where: { score: { min: 0.2 } }) { - score - ${personTypeLowerFirst} { - name - born - } + edges { + score + node { + name + born + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual(person2); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual(person1); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeLessThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual(person2); + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual(person1); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeLessThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(2); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(2); }); test("Limiting is possible", async () => { @@ -971,197 +1018,314 @@ describe("@fulltext directive", () => { return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a name", limit: 2) { - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "a name", first: 2) { + edges { + node { + name + born + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toBeArrayOfSize(2); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(2); }); - test("Offsetting is possible", async () => { + test("Sorting by score when the score is not returned", async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a name", offset: 2) { - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "a different name", sort: { score: ASC }) { + edges { + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: person3, + node: { + name: person3.name, + }, + }, + { + node: { + name: person1.name, + }, + }, + { + node: { + name: person2.name, + }, }, ]); }); - test("Combined limiting and offsetting is possible", async () => { + test("Sort by node when node is not returned", async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a name", limit: 1, offset: 1) { - score - ${personTypeLowerFirst} { - name - born - } + ${queryType}(phrase: "this is", sort: { node: { born: ASC } }) { + edges { + score + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual(person2); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[0].node).toBeUndefined(); + expect((gqlResult.data?.[queryType] as any).edges[1].node).toBeUndefined(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(2); }); - test("Sorting by score when the score is not returned", async () => { + test("Filters by node when node is not returned", async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", sort: { score: ASC }) { - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a different name", where: { node: { name_EQ: "${person1.name}" } }) { + edges { + score + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ - { - [personTypeLowerFirst]: { - name: person3.name, - }, - }, + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges[0].node).toBeUndefined(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); + }); + + test("Filters by score when no score is returned", async () => { + // Skip if multi-db not supported + if (!MULTIDB_SUPPORT) { + console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + const query = /* GraphQL */ ` + query { + ${queryType}(phrase: "a different name", where: { score: { max: 0.5 } }) { + edges { + node { + name + } + } + } + } + `; + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data?.[queryType] as any).edges).toEqual([ { - [personTypeLowerFirst]: { + node: { name: person1.name, }, }, { - [personTypeLowerFirst]: { - name: person2.name, + node: { + name: person3.name, }, }, ]); }); + }); + describe("Query Tests - limit required", () => { + let neoSchema: Neo4jGraphQL; + let personType: UniqueType; + let movieType: UniqueType; + let queryType: string; - test("Sort by node when node is not returned", async () => { + const person1 = { + name: "this is a name", + born: 1984, + }; + const person2 = { + name: "This is a different name", + born: 1985, + }; + const person3 = { + name: "Another name", + born: 1986, + }; + const movie1 = { + title: "Some Title", + description: "some other description", + released: 2001, + }; + const movie2 = { + title: "Another Title", + description: "this is a description", + released: 2002, + }; + + beforeEach(async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + personType = testHelper.createUniqueType("Person"); + movieType = testHelper.createUniqueType("Movie"); + queryType = `${personType.plural}ByName`; + + const typeDefs = generatedTypeDefs(personType, movieType); + + neoSchema = await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + limitRequired: true, + }, + }); + + await testHelper.createFulltextIndex(`${personType.name}Index`, personType.name, ["name"]); + + await neoSchema.getSchema(); + await neoSchema.assertIndexesAndConstraints({ + driver, + sessionConfig: { database: databaseName }, + }); + + await testHelper.executeCypher( + ` + CREATE (person1:${personType.name})-[:ACTED_IN]->(movie1:${movieType.name}) + CREATE (person1)-[:ACTED_IN]->(movie2:${movieType.name}) + CREATE (person2:${personType.name})-[:ACTED_IN]->(movie1) + CREATE (person3:${personType.name})-[:ACTED_IN]->(movie2) + SET person1 = $person1 + SET person2 = $person2 + SET person3 = $person3 + SET movie1 = $movie1 + SET movie2 = $movie2 + `, + { person1, person2, person3, movie1, movie2 } + ); + }); + + test("Limit argument provided", async () => { + // Skip if multi-db not supported + if (!MULTIDB_SUPPORT) { + console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "this is", sort: { ${personTypeLowerFirst}: { born: ASC } }) { - score + ${queryType}(phrase: "a different name", first: 2) { + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeNumber(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toBeUndefined(); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toBeUndefined(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(2); + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ + name: person2.name, + }); + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ + name: person1.name, + }); }); - test("Filters by node when node is not returned", async () => { + test("Limit argument not provided", async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { ${personTypeLowerFirst}: { name_EQ: "${person1.name}" } }) { - score + ${queryType}(phrase: "some name") { + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toBeUndefined(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${queryType}" argument "first" of type "Int!" is required, but it was not provided.`), + ]); }); - test("Filters by score when no score is returned", async () => { + test("Limit not provided on nested field", async () => { // Skip if multi-db not supported if (!MULTIDB_SUPPORT) { console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); return; } - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a different name", where: { score: { max: 0.5 } }) { - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a name", first: 10) { + edges { + node { + name + actedInMovies(sort: [{ released: ASC }]) { + title + released + } + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType]).toEqual([ - { - [personTypeLowerFirst]: { - name: person1.name, - }, - }, - { - [personTypeLowerFirst]: { - name: person3.name, - }, - }, - ]); + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "actedInMovies" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); }); describe("Query tests with auth", () => { let neoSchema: Neo4jGraphQL; let personType: UniqueType; let movieType: UniqueType; - let personTypeLowerFirst: string; let queryType: string; const person1 = { @@ -1196,8 +1360,7 @@ describe("@fulltext directive", () => { personType = testHelper.createUniqueType("Person"); movieType = testHelper.createUniqueType("Movie"); - queryType = `${personType.plural}Fulltext${upperFirst(personType.name)}Index`; - personTypeLowerFirst = personType.singular; + queryType = `${personType.plural}ByName`; await testHelper.executeCypher( ` @@ -1222,8 +1385,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + const typeDefs = /* GraphQL */ ` + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(filter: [{ where: { node: { name_EQ: "$jwt.name" } } }]) { name: String! born: Int! @@ -1258,10 +1421,12 @@ describe("@fulltext directive", () => { const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1271,11 +1436,11 @@ describe("@fulltext directive", () => { const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ name: person1.name, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); }); test("Works with @auth 'where' when unauthenticated", async () => { @@ -1285,8 +1450,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + const typeDefs = /* GraphQL */ ` + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(filter: [{ where: { node: { name_EQ: "$jwt.name" } } }]) { name: String! born: Int! @@ -1318,13 +1483,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1334,7 +1501,7 @@ describe("@fulltext directive", () => { const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect(gqlResult.errors).toBeFalsy(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(0); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(0); }); test("Works with @auth 'roles' when authenticated", async () => { @@ -1344,12 +1511,12 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) { name: String! born: Int! @@ -1381,13 +1548,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1397,22 +1566,22 @@ describe("@fulltext directive", () => { const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ name: person1.name, }); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ name: person2.name, }); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[2].node).toEqual({ name: person3.name, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD] ); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(3); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(3); }); test("Works with @auth 'roles' when unauthenticated", async () => { @@ -1422,12 +1591,12 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) { name: String! born: Int! @@ -1459,13 +1628,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1484,8 +1655,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + const typeDefs = /* GraphQL */ ` + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(validate: [{ when: BEFORE, where: { node: { name_EQ: "$jwt.name" } } }]) { name: String! born: Int! @@ -1517,13 +1688,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "a name", where: { ${personTypeLowerFirst}: { name_EQ: "${person2.name}" } }) { - score - ${personTypeLowerFirst} { - name - } + ${queryType}(phrase: "a name", where: { node: { name_EQ: "${person2.name}" } }) { + edges { + score + node { + name + } + } } } `; @@ -1533,9 +1706,9 @@ describe("@fulltext directive", () => { const gqlResult = await testHelper.executeGraphQLWithToken(query, token); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst].name).toBe(person2.name); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeNumber(); - expect(gqlResult.data?.[queryType] as any[]).toBeArrayOfSize(1); + expect((gqlResult.data?.[queryType] as any).edges[0].node.name).toBe(person2.name); + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeNumber(); + expect((gqlResult.data?.[queryType] as any).edges).toBeArrayOfSize(1); }); test("Works with @auth 'allow' when one match", async () => { @@ -1545,8 +1718,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + const typeDefs = /* GraphQL */ ` + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(validate: [{ when: BEFORE, where: { node: { name_EQ: "$jwt.name" } } }]) { name: String! born: Int! @@ -1578,13 +1751,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1603,12 +1778,12 @@ describe("@fulltext directive", () => { return; } - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type JWTPayload @jwt { roles: [String!]! } - type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", fields: ["name"] }]) + type ${personType.name} @node @fulltext(indexes: [{ indexName: "${personType.name}Index", queryName: "${queryType}", fields: ["name"] }]) @authorization(validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "admin" } } }]) { name: String! born: Int! @@ -1640,13 +1815,15 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; @@ -1665,16 +1842,15 @@ describe("@fulltext directive", () => { return; } - const moveTypeLowerFirst = movieType.singular; queryType = `${movieType.plural}Fulltext${upperFirst(movieType.name)}Index`; - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${personType.name} @node { name: String! born: Int! actedInMovies: [${movieType.name}!]! @relationship(type: "ACTED_IN", direction: OUT) } - type ${movieType.name} @node @fulltext(indexes: [{ indexName: "${movieType.name}Index", fields: ["title", "description"] }]) { + type ${movieType.name} @node @fulltext(indexes: [{ indexName: "${movieType.name}Index", queryName: "${movieType.plural}ByTitleAndDescription", fields: ["title", "description"] }]) { title: String! description: String released: Int! @@ -1694,30 +1870,34 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { - ${queryType}(phrase: "some description") { - score - ${moveTypeLowerFirst} { - title - description - } + ${movieType.plural}ByTitleAndDescription(phrase: "some description") { + edges { + score + node { + title + description + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][moveTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[`${movieType.plural}ByTitleAndDescription`] as any).edges[0].node).toEqual({ title: movie1.title, description: movie1.description, }); - expect((gqlResult.data?.[queryType] as any[])[1][moveTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[`${movieType.plural}ByTitleAndDescription`] as any).edges[1].node).toEqual({ title: movie2.title, description: movie2.description, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect( + (gqlResult.data?.[`${movieType.plural}ByTitleAndDescription`] as any).edges[0][SCORE_FIELD] + ).toBeGreaterThanOrEqual( + (gqlResult.data?.[`${movieType.plural}ByTitleAndDescription`] as any).edges[1][SCORE_FIELD] ); }); @@ -1729,7 +1909,6 @@ describe("@fulltext directive", () => { } personType = testHelper.createUniqueType("Person"); - personTypeLowerFirst = personType.singular; queryType = "CustomQueryName"; await testHelper.executeCypher( @@ -1744,7 +1923,7 @@ describe("@fulltext directive", () => { { person1, person2, person3 } ); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${personType.name} @node @fulltext(indexes: [{ queryName: "${queryType}", indexName: "${personType.name}CustomIndex", fields: ["name"] }]) { name: String! born: Int! @@ -1763,33 +1942,35 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "a different name") { - score - ${personTypeLowerFirst} { - name - } + edges { + score + node { + name + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ name: person2.name, }); - expect((gqlResult.data?.[queryType] as any[])[1][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ name: person1.name, }); - expect((gqlResult.data?.[queryType] as any[])[2][personTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[2].node).toEqual({ name: person3.name, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); - expect((gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[2][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[2][SCORE_FIELD] ); }); @@ -1800,9 +1981,8 @@ describe("@fulltext directive", () => { return; } - const moveTypeLowerFirst = movieType.singular; queryType = "SomeCustomQueryName"; - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${movieType.name} @node @fulltext(indexes: [{ queryName: "${queryType}", indexName: "${movieType.name}Index", fields: ["title", "description"] }]) { title: String! description: String @@ -1822,30 +2002,32 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query = ` + const query = /* GraphQL */ ` query { ${queryType}(phrase: "some description") { - score - ${moveTypeLowerFirst} { - title - description - } + edges { + score + node { + title + description + } + } } } `; const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult.data?.[queryType] as any[])[0][moveTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[0].node).toEqual({ title: movie1.title, description: movie1.description, }); - expect((gqlResult.data?.[queryType] as any[])[1][moveTypeLowerFirst]).toEqual({ + expect((gqlResult.data?.[queryType] as any).edges[1].node).toEqual({ title: movie2.title, description: movie2.description, }); - expect((gqlResult.data?.[queryType] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult.data?.[queryType] as any[])[1][SCORE_FIELD] + expect((gqlResult.data?.[queryType] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult.data?.[queryType] as any).edges[1][SCORE_FIELD] ); }); @@ -1857,7 +2039,6 @@ describe("@fulltext directive", () => { } movieType = testHelper.createUniqueType("Movie"); - const movieTypeLowerFirst = movieType.singular; const queryType1 = "CustomQueryName"; const queryType2 = "CustomQueryName2"; @@ -1871,7 +2052,7 @@ describe("@fulltext directive", () => { { movie1, movie2 } ); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${movieType.name} @node @fulltext(indexes: [ { queryName: "${queryType1}", indexName: "${movieType.name}CustomIndex", fields: ["title"] }, { queryName: "${queryType2}", indexName: "${movieType.name}CustomIndex2", fields: ["description"] } @@ -1894,23 +2075,27 @@ describe("@fulltext directive", () => { sessionConfig: { database: databaseName }, }); - const query1 = ` + const query1 = /* GraphQL */ ` query { ${queryType1}(phrase: "some title") { - score - ${movieTypeLowerFirst} { - title - } + edges { + score + node { + title + } + } } } `; - const query2 = ` + const query2 = /* GraphQL */ ` query { ${queryType2}(phrase: "some description") { - score - ${movieTypeLowerFirst} { - title - } + edges { + score + node { + title + } + } } } `; @@ -1918,25 +2103,25 @@ describe("@fulltext directive", () => { const gqlResult2 = await testHelper.executeGraphQL(query2); expect(gqlResult1.errors).toBeFalsy(); - expect((gqlResult1.data?.[queryType1] as any[])[0][movieTypeLowerFirst]).toEqual({ + expect((gqlResult1.data?.[queryType1] as any).edges[0].node).toEqual({ title: movie1.title, }); - expect((gqlResult1.data?.[queryType1] as any[])[1][movieTypeLowerFirst]).toEqual({ + expect((gqlResult1.data?.[queryType1] as any).edges[1].node).toEqual({ title: movie2.title, }); - expect((gqlResult1.data?.[queryType1] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult1.data?.[queryType1] as any[])[1][SCORE_FIELD] + expect((gqlResult1.data?.[queryType1] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult1.data?.[queryType1] as any).edges[1][SCORE_FIELD] ); expect(gqlResult2.errors).toBeFalsy(); - expect((gqlResult2.data?.[queryType2] as any[])[0][movieTypeLowerFirst]).toEqual({ + expect((gqlResult2.data?.[queryType2] as any).edges[0].node).toEqual({ title: movie1.title, }); - expect((gqlResult2.data?.[queryType2] as any[])[1][movieTypeLowerFirst]).toEqual({ + expect((gqlResult2.data?.[queryType2] as any).edges[1].node).toEqual({ title: movie2.title, }); - expect((gqlResult2.data?.[queryType2] as any[])[0][SCORE_FIELD]).toBeGreaterThanOrEqual( - (gqlResult2.data?.[queryType2] as any[])[1][SCORE_FIELD] + expect((gqlResult2.data?.[queryType2] as any).edges[0][SCORE_FIELD]).toBeGreaterThanOrEqual( + (gqlResult2.data?.[queryType2] as any).edges[1][SCORE_FIELD] ); }); }); @@ -1976,8 +2161,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = gql` - type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", fields: ["title"] }]) { + const typeDefs = /* GraphQL */ ` + type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", queryName: "${type.plural}ByTitle", fields: ["title"] }]) { title: String! } `; @@ -2000,8 +2185,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = gql` - type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", fields: ["title", "description"] }]) { + const typeDefs = /* GraphQL */ ` + type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) { title: String! description: String! } @@ -2031,8 +2216,8 @@ describe("@fulltext directive", () => { return; } - const typeDefs = gql` - type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", fields: ["title", "description"] }]) { + const typeDefs = /* GraphQL */ ` + type ${type.name} @node @fulltext(indexes: [{ indexName: "${indexName1}", queryName: "${type.plural}ByTitleAndDescription", fields: ["title", "description"] }]) { title: String! description: String! @alias(property: "${aliasName}") } @@ -2068,8 +2253,8 @@ describe("@fulltext directive", () => { const baseType = testHelper.createUniqueType("Base"); const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) @fulltext(indexes: [{ indexName: "${indexName1}", fields: ["title"] }]) { + const typeDefs = /* GraphQL */ ` + type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) @fulltext(indexes: [{ indexName: "${indexName1}", queryName: "${type.plural}ByTitle", fields: ["title"] }]) { title: String! } `; diff --git a/packages/graphql/tests/integration/directives/id.int.test.ts b/packages/graphql/tests/integration/directives/id.int.test.ts index 916763a12c..3bdbde279c 100644 --- a/packages/graphql/tests/integration/directives/id.int.test.ts +++ b/packages/graphql/tests/integration/directives/id.int.test.ts @@ -41,7 +41,7 @@ describe("@id directive", () => { test("should create a movie with autogenerate id", async () => { const typeDefs = ` type ${Movie} @node { - id: ID! @id @unique + id: ID! @id name: String } `; @@ -107,12 +107,12 @@ describe("@id directive", () => { test("should create a movie with autogenerate id and a nested genre with autogenerate id", async () => { const typeDefs = ` type ${Genre} @node { - id: ID! @id @unique + id: ID! @id name: String! } type ${Movie} @node { - id: ID! @id @unique + id: ID! @id name: String! genres: [${Genre}!]! @relationship(type: "HAS_GENRE", direction: OUT) } @@ -157,7 +157,7 @@ describe("@id directive", () => { test("should autogenerate an ID for a relationship property", async () => { const typeDefs = ` type ${Actor} @node { - id: ID! @id @unique + id: ID! @id name: String! } @@ -167,7 +167,7 @@ describe("@id directive", () => { } type ${Movie} @node { - id: ID! @id @unique + id: ID! @id title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } diff --git a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts index 8458e7b536..1df2cd5d7a 100644 --- a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts +++ b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts @@ -1747,7 +1747,7 @@ describe("@populatedBy directive - Node properties", () => { [testMovie.plural]: [ { id: movieId, - callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, ], }, @@ -1804,7 +1804,7 @@ describe("@populatedBy directive - Node properties", () => { [testMovie.plural]: [ { id: movieId, - callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, ], }, diff --git a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts index f9906839db..6d0dfc8ae7 100644 --- a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts +++ b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts @@ -928,7 +928,7 @@ describe("@populatedBy directive - Relationship properties", () => { genres: { update: { edge: { - id: "${relId}" + id_SET: "${relId}" } } } @@ -2439,7 +2439,7 @@ describe("@populatedBy directive - Relationship properties", () => { description: "@populatedBy - Time", type: "Time", callback: () => Promise.resolve(`${date.toISOString().split("T")[1]}`), - expectedValue: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + expectedValue: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, { description: "@populatedBy - LocalDateTime", diff --git a/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-create.int.test.ts b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-create.int.test.ts new file mode 100644 index 0000000000..7de165176f --- /dev/null +++ b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-create.int.test.ts @@ -0,0 +1,158 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Create: Multiple relationships results difference between Connection API and Simple API", () => { + const testHelper: TestHelper = new TestHelper(); + const Movie: UniqueType = testHelper.createUniqueType("Movie"); + const Actor: UniqueType = testHelper.createUniqueType("Actor"); + + beforeEach(async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Actor} @node { + name: String! + movies: [${Movie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + role: String! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return multiple relationship results for connection API", async () => { + const source = /* GraphQL */ ` + mutation { + ${Movie.operations.create}( + input: [ + { + title: "Movie One" + actors: { + create: [{ edge: { role: "Role One" }, node: { name: "Actor One" } }] + connect: [{ edge: { role: "Role Two" } }] + } + } + ] + ) { + ${Movie.plural} { + title + actorsConnection { + edges { + properties { + role + } + node { + name + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.operations.create]: { + [Movie.plural]: [ + { + title: "Movie One", + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: "Actor One", + }, + properties: { + role: "Role One", + }, + }, + { + node: { + name: "Actor One", + }, + properties: { + role: "Role Two", + }, + }, + ]), + }, + }, + ], + }, + }); + }); + + test("should only return a single relationship result for simple API", async () => { + const source = /* GraphQL */ ` + mutation { + ${Movie.operations.create}( + input: [ + { + title: "Movie One" + actors: { + create: [{ edge: { role: "Role One" }, node: { name: "Actor One" } }] + connect: [{ edge: { role: "Role Two" } }] + } + } + ] + ) { + ${Movie.plural} { + title + actors { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.operations.create]: { + [Movie.plural]: [ + { + title: "Movie One", + actors: [ + { + name: "Actor One", + }, + ], + }, + ], + }, + }); + }); +}); diff --git a/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-interface.int.test.ts b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-interface.int.test.ts new file mode 100644 index 0000000000..19be6bbee2 --- /dev/null +++ b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-interface.int.test.ts @@ -0,0 +1,220 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Interface: Multiple relationships results difference between Connection API and Simple API", () => { + const testHelper: TestHelper = new TestHelper(); + + const Production: UniqueType = testHelper.createUniqueType("Production"); + const Movie: UniqueType = testHelper.createUniqueType("Movie"); + const Series: UniqueType = testHelper.createUniqueType("Series"); + const Actor: UniqueType = testHelper.createUniqueType("Actor"); + + beforeAll(async () => { + const typeDefs = /* GraphQL */ ` + interface ${Production} { + title: String! + } + + type ${Movie} implements ${Production} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Series} implements ${Production} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Actor} @node { + name: String! + productions: [${Production}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + role: String! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + // Create duplicate relationships + await testHelper.executeCypher(` + CREATE (m:${Movie} {title: "Movie One"}) + + CREATE (s:${Series} {title: "Series One"}) + + CREATE (a:${Actor} {name: "Actor One"}) + CREATE (a)-[:ACTED_IN {role: "Movie role one"}]->(m) + CREATE (a)-[:ACTED_IN {role: "Movie role two"}]->(m) + + CREATE (a)-[:ACTED_IN {role: "Series role one"}]->(s) + CREATE (a)-[:ACTED_IN {role: "Series role two"}]->(s) + `); + }); + + afterAll(async () => { + await testHelper.close(); + }); + + test("should return multiple relationship results for connection API", async () => { + const source = /* GraphQL */ ` + query { + ${Production.operations.connection} { + edges { + node { + ...on ${Movie} { + title + actorsConnection { + edges { + node { + name + } + properties { + role + } + } + } + } + ...on ${Series} { + title + actorsConnection { + edges { + node { + name + } + properties { + role + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Production.operations.connection]: { + edges: expect.toIncludeSameMembers([ + { + node: { + title: "Movie One", + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: "Actor One", + }, + properties: { + role: "Movie role one", + }, + }, + { + node: { + name: "Actor One", + }, + properties: { + role: "Movie role two", + }, + }, + ]), + }, + }, + }, + { + node: { + title: "Series One", + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: "Actor One", + }, + properties: { + role: "Series role one", + }, + }, + { + node: { + name: "Actor One", + }, + properties: { + role: "Series role two", + }, + }, + ]), + }, + }, + }, + ]), + }, + }); + }); + + test("should only return a single relationship result for simple API", async () => { + const source = /* GraphQL */ ` + query { + ${Production.plural} { + ...on ${Movie} { + title + actors { + name + } + } + ...on ${Series} { + title + actors { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Production.plural]: expect.toIncludeSameMembers([ + { + title: "Movie One", + actors: [ + { + name: "Actor One", + }, + ], + }, + { + title: "Series One", + actors: [ + { + name: "Actor One", + }, + ], + }, + ]), + }); + }); +}); diff --git a/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-union.int.test.ts b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-union.int.test.ts new file mode 100644 index 0000000000..4691d9580d --- /dev/null +++ b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read-union.int.test.ts @@ -0,0 +1,189 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Union: Multiple relationships results difference between Connection API and Simple API", () => { + const testHelper: TestHelper = new TestHelper(); + + const Production: UniqueType = testHelper.createUniqueType("Production"); + const Movie: UniqueType = testHelper.createUniqueType("Movie"); + const Series: UniqueType = testHelper.createUniqueType("Series"); + const Actor: UniqueType = testHelper.createUniqueType("Actor"); + + beforeAll(async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Series} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + union ${Production} = ${Movie} | ${Series} + + type ${Actor} @node { + name: String! + productions: [${Production}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + role: String! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + // Create duplicate relationships + await testHelper.executeCypher(` + CREATE (m:${Movie} {title: "Movie One"}) + + CREATE (s:${Series} {title: "Series One"}) + + CREATE (a:${Actor} {name: "Actor One"}) + CREATE (a)-[:ACTED_IN {role: "Movie role one"}]->(m) + CREATE (a)-[:ACTED_IN {role: "Movie role two"}]->(m) + + CREATE (a)-[:ACTED_IN {role: "Series role one"}]->(s) + CREATE (a)-[:ACTED_IN {role: "Series role two"}]->(s) + `); + }); + + afterAll(async () => { + await testHelper.close(); + }); + + test("should return multiple relationship results for connection API", async () => { + const source = /* GraphQL */ ` + query { + ${Actor.operations.connection} { + edges { + node { + productionsConnection { + edges { + node { + ... on ${Movie} { + title + } + ... on ${Series} { + title + } + } + properties { + role + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Actor.operations.connection]: { + edges: [ + { + node: { + productionsConnection: { + edges: [ + { + node: { + title: "Movie One", + }, + properties: { + role: "Movie role one", + }, + }, + { + node: { + title: "Movie One", + }, + properties: { + role: "Movie role two", + }, + }, + { + node: { + title: "Series One", + }, + properties: { + role: "Series role one", + }, + }, + { + node: { + title: "Series One", + }, + properties: { + role: "Series role two", + }, + }, + ], + }, + }, + }, + ], + }, + }); + }); + + test("should only return a single relationship result for simple API", async () => { + const source = /* GraphQL */ ` + query { + ${Actor.plural} { + productions { + ...on ${Movie} { + title + } + ...on ${Series} { + title + } + } + name + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Actor.plural]: [ + { + productions: expect.toIncludeSameMembers([ + { + title: "Movie One", + }, + { + title: "Series One", + }, + ]), + name: "Actor One", + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read.int.test.ts b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read.int.test.ts new file mode 100644 index 0000000000..208eb0a984 --- /dev/null +++ b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-read.int.test.ts @@ -0,0 +1,138 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Multiple relationships results difference between Connection API and Simple API", () => { + const testHelper: TestHelper = new TestHelper(); + const Movie: UniqueType = testHelper.createUniqueType("Movie"); + const Actor: UniqueType = testHelper.createUniqueType("Actor"); + + beforeAll(async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Actor} @node { + name: String! + movies: [${Movie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + role: String! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + // Create duplicate relationships + await testHelper.executeCypher(` + CREATE (m:${Movie} {title: "Movie One"}) + CREATE (a:${Actor} {name: "Actor One"}) + CREATE (a)-[:ACTED_IN {role: "Role One"}]->(m) + CREATE (a)-[:ACTED_IN {role: "Role Two"}]->(m) + `); + }); + + afterAll(async () => { + await testHelper.close(); + }); + + test("should return multiple relationship results for connection API", async () => { + const source = /* GraphQL */ ` + query { + ${Movie.plural} { + title + actorsConnection { + edges { + node { + name + } + properties { + role + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.plural]: [ + { + title: "Movie One", + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: "Actor One", + }, + properties: { + role: "Role One", + }, + }, + { + node: { + name: "Actor One", + }, + properties: { + role: "Role Two", + }, + }, + ]), + }, + }, + ], + }); + }); + + test("should only return a single relationship result for simple API", async () => { + const source = /* GraphQL */ ` + query { + ${Movie.plural} { + title + actors { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.plural]: [ + { + title: "Movie One", + actors: [ + { + name: "Actor One", + }, + ], + }, + ], + }); + }); +}); diff --git a/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-update.int.test.ts b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-update.int.test.ts new file mode 100644 index 0000000000..a23dbabf85 --- /dev/null +++ b/packages/graphql/tests/integration/directives/relationship/duplicate-relationship/duplicate-relationship-update.int.test.ts @@ -0,0 +1,173 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { UniqueType } from "../../../../utils/graphql-types"; +import { TestHelper } from "../../../../utils/tests-helper"; + +describe("Update: Multiple relationships results difference between Connection API and Simple API", () => { + const testHelper: TestHelper = new TestHelper(); + const Movie: UniqueType = testHelper.createUniqueType("Movie"); + const Actor: UniqueType = testHelper.createUniqueType("Actor"); + + beforeEach(async () => { + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + title: String! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + } + + type ${Actor} @node { + name: String! + movies: [${Movie}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type ActedIn @relationshipProperties { + role: String! + } + `; + await testHelper.initNeo4jGraphQL({ typeDefs }); + + // Create duplicate relationships + await testHelper.executeCypher(` + CREATE (m:${Movie} {title: "Movie One"}) + CREATE (a:${Actor} {name: "Actor One"}) + CREATE (a)-[:ACTED_IN {role: "Role One"}]->(m) + `); + }); + + afterEach(async () => { + await testHelper.close(); + }); + + test("should return multiple relationship results for connection API", async () => { + const source = /* GraphQL */ ` + mutation { + ${Movie.operations.update}( + where: { title: { eq: "Movie One" } } + update: { + actors: [ + { + connect: [ + { + where: { node: { name: { eq: "Actor One" } } } + edge: { role: "Role Two" } + } + ] + } + ] + } + ) { + ${Movie.plural} { + title + actorsConnection { + edges { + properties { + role + } + node { + name + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.operations.update]: { + [Movie.plural]: [ + { + title: "Movie One", + actorsConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { + name: "Actor One", + }, + properties: { + role: "Role One", + }, + }, + { + node: { + name: "Actor One", + }, + properties: { + role: "Role Two", + }, + }, + ]), + }, + }, + ], + }, + }); + }); + + test("should only return a single relationship result for simple API", async () => { + const source = /* GraphQL */ ` + mutation { + ${Movie.operations.update}( + where: { title: { eq: "Movie One" } } + update: { + actors: [ + { + connect: [ + { + where: { node: { name: { eq: "Actor One" } } } + edge: { role: "Role Two" } + } + ] + } + ] + } + ) { + ${Movie.plural} { + title + actors { + name + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(source); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [Movie.operations.update]: { + [Movie.plural]: [ + { + title: "Movie One", + actors: [ + { + name: "Actor One", + }, + ], + }, + ], + }, + }); + }); +}); diff --git a/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts b/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts index 392f0e9a31..e4773f7104 100644 --- a/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts +++ b/packages/graphql/tests/integration/directives/relationship/nestedOperations.int.test.ts @@ -38,10 +38,8 @@ describe("@relationhip - nestedOperations", () => { describe("Related to a concrete type", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -66,25 +64,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; + updateMutationWithNestedCreate = `#graphql mutation { ${Movie.operations.update}(update: { actors: { create: { node: { name: "someName" } } } }) { @@ -107,25 +87,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -184,14 +146,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -204,19 +162,13 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeFalsy(); expect(updateWithNestedConnectResult.errors).toBeDefined(); expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -250,14 +202,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -270,86 +218,13 @@ describe("@relationhip - nestedOperations", () => { 'Field "create" is not defined by type' ); expect(createWithNestedConnectResult.errors).toBeFalsy(); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' ); expect(updateWithNestedConnectResult.errors).toBeFalsy(); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeDefined(); - expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( - 'Field "disconnect" is not defined by type' - ); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the connectOrCreate nested op when CONNECT_OR_CREATE is the only nestedOperation specified", async () => { - const typeDefs = `#graphql - type ${Person} @node { - id: ID! @id @unique - name: String - } - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -383,14 +258,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -406,10 +277,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -418,10 +286,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeFalsy(); expect(updateWithNestedDisconnectResult.errors).toBeDefined(); expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( @@ -452,14 +317,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -475,10 +336,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -487,10 +345,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -521,14 +376,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -544,10 +395,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -556,10 +404,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -579,10 +424,9 @@ describe("@relationhip - nestedOperations", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; + let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -610,27 +454,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; + updateMutationWithNestedCreate = `#graphql mutation { ${Movie.operations.update}(update: { actors: { ${PersonOne}: { create: { node: { name: "someName" } } } } }) { @@ -653,27 +477,7 @@ describe("@relationhip - nestedOperations", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -737,14 +541,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -757,19 +557,13 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeFalsy(); expect(updateWithNestedConnectResult.errors).toBeDefined(); expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -809,14 +603,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -829,92 +619,13 @@ describe("@relationhip - nestedOperations", () => { 'Field "create" is not defined by type' ); expect(createWithNestedConnectResult.errors).toBeFalsy(); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' ); expect(updateWithNestedConnectResult.errors).toBeFalsy(); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeDefined(); - expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( - 'Field "disconnect" is not defined by type' - ); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the connectOrCreate nested op when CONNECT_OR_CREATE is the only nestedOperation specified", async () => { - const typeDefs = `#graphql - type ${PersonOne} @node { - id: ID! @id @unique - name: String - } - - type ${PersonTwo} @node { - nameTwo: String - } - - union ${Person} = ${PersonOne} | ${PersonTwo} - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -954,14 +665,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -977,10 +684,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -989,10 +693,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeFalsy(); expect(updateWithNestedDisconnectResult.errors).toBeDefined(); expect((updateWithNestedDisconnectResult.errors as any)[0].message).toInclude( @@ -1029,14 +730,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -1052,10 +749,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -1064,10 +758,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -1104,14 +795,10 @@ describe("@relationhip - nestedOperations", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -1127,10 +814,7 @@ describe("@relationhip - nestedOperations", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); + expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -1139,10 +823,7 @@ describe("@relationhip - nestedOperations", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -1157,7 +838,6 @@ describe("@relationhip - nestedOperations", () => { }); describe("Related to an interface type", () => { - // TODO: add tests/expects for connectOrCreate once implemented for interfaces let PersonOne: UniqueType; let PersonTwo: UniqueType; diff --git a/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts b/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts index fff48effe4..243f69bbfe 100644 --- a/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts +++ b/packages/graphql/tests/integration/directives/relationship/query-direction.int.test.ts @@ -62,7 +62,7 @@ describe("query-direction", () => { test("should return related node using the queryDirection DIRECTED", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { name: "${mike}" }) { + ${Person.plural}(where: { name_EQ: "${mike}" }) { name friends { name @@ -96,7 +96,7 @@ describe("query-direction", () => { test("should return person with friend named Mike (directed out)", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { + ${Person.plural}(where: { friends_SOME: { name_EQ: "${mike}" } }) { name } } @@ -122,7 +122,7 @@ describe("query-direction", () => { test("should delete two nodes when performing nested delete under delete (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { + ${Person.operations.delete}(where: { name_EQ: "${mike}" }, delete: { friends: { where: { } } }) { nodesDeleted } } @@ -146,7 +146,7 @@ describe("query-direction", () => { test("should only delete one when performing nested delete under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { delete: { where: { } } } }) { ${Person.plural} { name friends { @@ -186,7 +186,7 @@ describe("query-direction", () => { test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { ${Person.plural} { name friends { @@ -226,217 +226,7 @@ describe("query-direction", () => { test("should only update one when performing nested update under update (always directed)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { - ${Person.plural} { - name - friends { - name - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: "Bob" }]), - }, - ]), - }, - }); - }); - }); - - describe("DEFAULT_DIRECTED", () => { - beforeEach(async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - test("should return related node using the queryDirection DIRECTED", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { name: "${mike}" }) { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: charlie, - }, - ]), - }, - ]), - }); - }); - - test("should return person with friend named Mike (directed out)", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - }, - ]), - }); - }); - - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { - nodesDeleted - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.delete]: { - nodesDeleted: 2, - }, - }); - }); - - test("should only delete one when performing nested delete under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - nodesDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: [], - }, - ]), - info: { - nodesDeleted: 1, - }, - }, - }); - }); - - test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - relationshipsDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: [], - }, - ]), - info: { - relationshipsDeleted: 1, - }, - }, - }); - }); - - test("should only update one when performing nested update under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { update: { node: { name_SET: "Bob" } } } }) { ${Person.plural} { name friends { @@ -482,223 +272,7 @@ describe("query-direction", () => { test("should return related node using the queryDirection UNDIRECTED", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { name: "${mike}" }) { - name - friends { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }, - ]), - }); - }); - - test("should return person with friend named Mike (undirected)", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { - name - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.plural]: expect.toIncludeSameMembers([ - { - name: stefan, - }, - { - name: charlie, - }, - ]), - }); - }); - - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { - nodesDeleted - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.delete]: { - nodesDeleted: 2, - }, - }); - }); - - test("should only delete one when performing nested delete under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - nodesDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), - info: { - nodesDeleted: 1, - }, - }, - }); - }); - - test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { - ${Person.plural} { - name - friends { - name - } - } - info { - relationshipsDeleted - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), - info: { - relationshipsDeleted: 1, - }, - }, - }); - }); - - test("should only update one when performing nested update under update (always directed)", async () => { - const query = /* GraphQL */ ` - mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { - ${Person.plural} { - name - friends { - name - } - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - - if (gqlResult.errors) { - console.log(JSON.stringify(gqlResult.errors, null, 2)); - } - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data).toEqual({ - [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }, { name: "Bob" }]), - }, - ]), - }, - }); - }); - }); - - describe("DEFAULT_UNDIRECTED", () => { - beforeEach(async () => { - const typeDefs = /* GraphQL */ ` - type ${Person} @node { - name: String! - friends: [${Person}!]! @relationship(type: "HAS_FRIEND", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - test("should return related node using the queryDirection UNDIRECTED", async () => { - const query = /* GraphQL */ ` - { - ${Person.plural}(where: { name: "${mike}" }) { + ${Person.plural}(where: { name_EQ: "${mike}" }) { name friends { name @@ -735,7 +309,7 @@ describe("query-direction", () => { test("should return person with friend named Mike (undirected)", async () => { const query = /* GraphQL */ ` { - ${Person.plural}(where: { friends_SOME: { name: "${mike}" } }) { + ${Person.plural}(where: { friends_SOME: { name_EQ: "${mike}" } }) { name } } @@ -761,10 +335,10 @@ describe("query-direction", () => { }); }); - test("should delete two nodes when performing nested delete under delete (always directed)", async () => { + test("should delete three nodes when performing nested delete under delete (undirected)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.delete}(where: { name: "${mike}" }, delete: { friends: { where: { } } }) { + ${Person.operations.delete}(where: { name_EQ: "${mike}" }, delete: { friends: { where: { } } }) { nodesDeleted } } @@ -780,15 +354,15 @@ describe("query-direction", () => { expect(gqlResult.data).toEqual({ [Person.operations.delete]: { - nodesDeleted: 2, + nodesDeleted: 3, }, }); }); - test("should only delete one when performing nested delete under update (always directed)", async () => { + test("should delete both connected nodes when performing nested delete under update (undirected)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { delete: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { delete: { where: { } } } }) { ${Person.plural} { name friends { @@ -812,23 +386,18 @@ describe("query-direction", () => { expect(gqlResult.data).toEqual({ [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), + [Person.plural]: [{ name: mike, friends: [] }], info: { - nodesDeleted: 1, + nodesDeleted: 2, }, }, }); }); - test("should only disconnect one when performing nested disconnect under update (always directed)", async () => { + test("should disconnect both connected nodes when performing nested disconnect under update (undirected)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { disconnect: { where: { } } } }) { ${Person.plural} { name friends { @@ -852,23 +421,18 @@ describe("query-direction", () => { expect(gqlResult.data).toEqual({ [Person.operations.update]: { - [Person.plural]: expect.toIncludeSameMembers([ - { - name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }]), - }, - ]), + [Person.plural]: [{ name: mike, friends: [] }], info: { - relationshipsDeleted: 1, + relationshipsDeleted: 2, }, }, }); }); - test("should only update one when performing nested update under update (always directed)", async () => { + test("should update both nodes when performing nested update under update (undirected)", async () => { const query = /* GraphQL */ ` mutation { - ${Person.operations.update}(where: { name: "${mike}" }, update: { friends: { update: { node: { name: "Bob" } } } }) { + ${Person.operations.update}(where: { name_EQ: "${mike}" }, update: { friends: { update: { node: { name_SET: "Bob" } } } }) { ${Person.plural} { name friends { @@ -892,7 +456,7 @@ describe("query-direction", () => { [Person.plural]: expect.toIncludeSameMembers([ { name: mike, - friends: expect.toIncludeSameMembers([{ name: stefan }, { name: "Bob" }]), + friends: expect.toIncludeSameMembers([{ name: "Bob" }, { name: "Bob" }]), }, ]), }, diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts index 3dfa199f54..00b18d5b49 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-database-name.int.test.ts @@ -34,19 +34,19 @@ describe("RelayId projection with different database name", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") title: String! - genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) + genre: [${Genre}!]! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId @alias(property: "serverId") + dbId: ID! @id @relayId @alias(property: "serverId") name: String! } `; @@ -101,11 +101,13 @@ describe("RelayId projection with different database name", () => { id: expect.toBeString(), dbId: expect.toBeString(), title: "Movie1", - genre: { - id: expect.toBeString(), - dbId: expect.toBeString(), - name: "Action", - }, + genre: [ + { + id: expect.toBeString(), + dbId: expect.toBeString(), + name: "Action", + }, + ], actors: [ { id: expect.toBeString(), @@ -120,8 +122,8 @@ describe("RelayId projection with different database name", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (queryResult.data as any)?.[Movie.plural][0].genre?.id; - const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre?.dbId; + const genreId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.id; + const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.dbId; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); @@ -169,11 +171,13 @@ describe("RelayId projection with different database name", () => { id: expect.toBeString(), dbId: expect.toBeString(), title: "Movie1", - genre: { - id: expect.toBeString(), - dbId: expect.toBeString(), - name: "Action", - }, + genre: [ + { + id: expect.toBeString(), + dbId: expect.toBeString(), + name: "Action", + }, + ], actors: [ { id: expect.toBeString(), @@ -191,8 +195,9 @@ describe("RelayId projection with different database name", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre?.id; - const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre + const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] + ?.id; + const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] ?.dbId; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts index ba6134878b..0ed7318006 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection-with-field-alias.int.test.ts @@ -35,19 +35,19 @@ describe("RelayId projection with GraphQL field alias", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId title: String! - genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) + genre: [${Genre}!]! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } `; @@ -102,11 +102,13 @@ describe("RelayId projection with GraphQL field alias", () => { testAliasID: expect.toBeString(), testAliasDBID: expect.toBeString(), title: "Movie1", - genre: { - testAliasID: expect.toBeString(), - testAliasDBID: expect.toBeString(), - name: "Action", - }, + genre: [ + { + testAliasID: expect.toBeString(), + testAliasDBID: expect.toBeString(), + name: "Action", + }, + ], actors: [ { testAliasID: expect.toBeString(), @@ -121,8 +123,8 @@ describe("RelayId projection with GraphQL field alias", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (queryResult.data as any)?.[Movie.plural][0].genre?.testAliasID; - const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre?.testAliasDBID; + const genreId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.testAliasID; + const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.testAliasDBID; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); @@ -170,11 +172,13 @@ describe("RelayId projection with GraphQL field alias", () => { testAliasID: expect.toBeString(), testAliasDBID: expect.toBeString(), title: "Movie1", - genre: { - testAliasID: expect.toBeString(), - testAliasDBID: expect.toBeString(), - name: "Action", - }, + genre: [ + { + testAliasID: expect.toBeString(), + testAliasDBID: expect.toBeString(), + name: "Action", + }, + ], actors: [ { testAliasID: expect.toBeString(), @@ -192,9 +196,9 @@ describe("RelayId projection with GraphQL field alias", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre + const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] ?.testAliasID; - const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre + const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] ?.testAliasDBID; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); diff --git a/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts b/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts index cc73e1e00e..48b27d057b 100644 --- a/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts +++ b/packages/graphql/tests/integration/directives/relayId/relayId-projection.int.test.ts @@ -34,19 +34,19 @@ describe("RelayId projection", () => { beforeAll(async () => { const typeDefs = ` type ${Movie} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId title: String! - genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) + genre: [${Genre}!]! @relationship(type: "HAS_GENRE", direction: OUT) actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) } type ${Genre} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } type ${Actor} @node { - dbId: ID! @id @unique @relayId + dbId: ID! @id @relayId name: String! } `; @@ -100,11 +100,13 @@ describe("RelayId projection", () => { id: expect.toBeString(), dbId: expect.toBeString(), title: "Movie1", - genre: { - id: expect.toBeString(), - dbId: expect.toBeString(), - name: "Action", - }, + genre: [ + { + id: expect.toBeString(), + dbId: expect.toBeString(), + name: "Action", + }, + ], actors: [ { id: expect.toBeString(), @@ -119,8 +121,8 @@ describe("RelayId projection", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (queryResult.data as any)?.[Movie.plural][0].genre?.id; - const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre?.dbId; + const genreId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.id; + const genreDbId = (queryResult.data as any)?.[Movie.plural][0].genre[0]?.dbId; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); @@ -168,11 +170,13 @@ describe("RelayId projection", () => { id: expect.toBeString(), dbId: expect.toBeString(), title: "Movie1", - genre: { - id: expect.toBeString(), - dbId: expect.toBeString(), - name: "Action", - }, + genre: [ + { + id: expect.toBeString(), + dbId: expect.toBeString(), + name: "Action", + }, + ], actors: [ { id: expect.toBeString(), @@ -190,8 +194,9 @@ describe("RelayId projection", () => { expect(dbId).toBe(movieDatabaseID); expect(id).toBe(toGlobalId({ typeName: Movie.name, field: "dbId", id: dbId })); - const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre?.id; - const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre + const genreId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] + ?.id; + const genreDbId = (connectionQueryResult.data as any)?.[Movie.operations.connection]?.edges[0]?.node?.genre[0] ?.dbId; expect(genreDbId).toBe(genreDatabaseID); expect(genreId).toBe(toGlobalId({ typeName: Genre.name, field: "dbId", id: genreDbId })); diff --git a/packages/graphql/tests/integration/directives/unique.int.test.ts b/packages/graphql/tests/integration/directives/unique.int.test.ts deleted file mode 100644 index a2e41f0482..0000000000 --- a/packages/graphql/tests/integration/directives/unique.int.test.ts +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Driver } from "neo4j-driver"; -import { generate } from "randomstring"; -import type { Neo4jDatabaseInfo } from "../../../src/classes/Neo4jDatabaseInfo"; -import { isMultiDbUnsupportedError } from "../../utils/is-multi-db-unsupported-error"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("assertIndexesAndConstraints/unique", () => { - const testHelper = new TestHelper(); - let driver: Driver; - let databaseName: string; - let MULTIDB_SUPPORT = true; - let dbInfo: Neo4jDatabaseInfo; - - beforeAll(async () => { - dbInfo = await testHelper.getDatabaseInfo(); - - databaseName = generate({ readable: true, charset: "alphabetic" }); - - try { - await testHelper.createDatabase(databaseName); - } catch (e) { - if (e instanceof Error) { - if (isMultiDbUnsupportedError(e)) { - // No multi-db support, so we skip tests - MULTIDB_SUPPORT = false; - await testHelper.close(); - } else { - throw e; - } - } - } - }); - - beforeEach(async () => { - if (MULTIDB_SUPPORT) { - driver = await testHelper.getDriver(); - } - }); - - afterEach(async () => { - if (MULTIDB_SUPPORT) { - await testHelper.close(); - } - }); - - afterAll(async () => { - if (MULTIDB_SUPPORT) { - await testHelper.dropDatabase(); - await testHelper.close(); - } - }); - - describe("@unique", () => { - test("should throw an error when all necessary constraints do not exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.isbn`); - }); - - test("should throw an error when all necessary constraints do not exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique @alias(property: "internationalStandardBookNumber") - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.internationalStandardBookNumber`); - }); - - test("should not throw an error when all necessary constraints exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_isbn ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.isbn IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw an error when all necessary constraints exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("Book"); - - const typeDefs = ` - type ${type.name} @node { - isbn: String! @unique @alias(property: "internationalStandardBookNumber") - title: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_isbn ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.internationalStandardBookNumber IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw if constraint exists on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someIntProperty: Int! - title: String! @unique - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_title ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.title IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ - driver, - sessionConfig: { database: databaseName }, - }) - ).resolves.not.toThrow(); - }); - - test("should not allow creating duplicate @unique properties when constraint is on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someStringProperty: String! @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const mutation = ` - mutation { - ${baseType.operations.create}(input: [ - { - someStringProperty: "notUnique", - title: "someTitle" - }, - { - someStringProperty: "notUnique", - title: "someTitle2" - }, - ]) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const query = ` - query { - ${baseType.plural} { - someStringProperty - } - } - `; - - const mutationGqlResult = await testHelper.executeGraphQL(mutation); - - const queryGqlResult = await testHelper.executeGraphQL(query); - - expect((mutationGqlResult?.errors as any[])[0].message).toBe("Constraint validation failed"); - - expect(queryGqlResult.errors).toBeFalsy(); - expect(queryGqlResult.data?.[baseType.plural]).toBeArrayOfSize(0); - }); - - test("should not allow updating to duplicate @unique properties when constraint is on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) { - someStringProperty: String! @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const uniqueVal1 = "someVal1"; - const uniqueVal2 = "someUniqueVal2"; - - const createMutation = ` - mutation { - ${baseType.operations.create}(input: [ - { - someStringProperty: "${uniqueVal1}", - title: "someTitle" - }, - { - someStringProperty: "${uniqueVal2}", - title: "someTitle2" - }, - ]) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const updateMutation = /* GraphQL */ ` - mutation { - ${baseType.operations.update}(update: { - someStringProperty_SET: "notUnique" - }) { - ${baseType.plural} { - someStringProperty - } - } - } - `; - - const query = ` - query { - ${baseType.plural} { - someStringProperty - } - } - `; - - const createGqlResult = await testHelper.executeGraphQL(createMutation); - const updateGqlResult = await testHelper.executeGraphQL(updateMutation); - const queryGqlResult = await testHelper.executeGraphQL(query); - - expect(createGqlResult?.errors).toBeFalsy(); - expect((updateGqlResult?.errors as any[])[0].message).toBe("Constraint validation failed"); - - expect(queryGqlResult.errors).toBeFalsy(); - expect(queryGqlResult.data?.[baseType.plural]).toIncludeSameMembers([ - { - someStringProperty: uniqueVal1, - }, - { - someStringProperty: uniqueVal2, - }, - ]); - }); - }); - - describe("@id", () => { - test("should throw an error when all necessary constraints do not exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.id`); - }); - - test("should throw an error when all necessary constraints do not exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique @alias(property: "identifier") - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).rejects.toThrow(`Missing constraint for ${type.name}.identifier`); - }); - - test("should not throw an error when all necessary constraints exist", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_id ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.id IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw an error when all necessary constraints exist when used with @alias", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const type = testHelper.createUniqueType("User"); - - const typeDefs = ` - type ${type.name} @node { - id: ID! @id @unique @alias(property: "identifier") - name: String! - } - `; - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - const cypher = `CREATE CONSTRAINT ${type.name}_id ${dbInfo.gte("4.4") ? "FOR" : "ON"} (n:${type.name}) ${ - dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT" - } n.identifier IS UNIQUE`; - - await testHelper.executeCypher(cypher); - - await expect( - neoSchema.assertIndexesAndConstraints({ driver, sessionConfig: { database: databaseName } }) - ).resolves.not.toThrow(); - }); - - test("should not throw if constraint exists on an additional label", async () => { - // Skip if multi-db not supported - if (!MULTIDB_SUPPORT) { - console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); - return; - } - - const baseType = testHelper.createUniqueType("Base"); - const additionalType = testHelper.createUniqueType("Additional"); - const typeDefs = ` - type ${baseType.name} @node(labels: ["${baseType.name}", "${additionalType.name}"]) @mutation(operations: []) { - someIdProperty: ID! @id @unique @alias(property: "someAlias") - title: String! - } - `; - - const createConstraintCypher = ` - CREATE CONSTRAINT ${baseType.name}_unique_someAlias ${dbInfo.gte("4.4") ? "FOR" : "ON"} (r:${ - additionalType.name - }) - ${dbInfo.gte("4.4") ? "REQUIRE" : "ASSERT"} r.someAlias IS UNIQUE; - `; - - await testHelper.executeCypher(createConstraintCypher); - - const neoSchema = await testHelper.initNeo4jGraphQL({ typeDefs }); - await neoSchema.getSchema(); - - await expect( - neoSchema.assertIndexesAndConstraints({ - driver, - sessionConfig: { database: databaseName }, - }) - ).resolves.not.toThrow(); - }); - }); -}); diff --git a/packages/graphql/tests/integration/directives/vector/vector-auth.int.test.ts b/packages/graphql/tests/integration/directives/vector/vector-auth.int.test.ts index e6aa97292e..e43640279d 100644 --- a/packages/graphql/tests/integration/directives/vector/vector-auth.int.test.ts +++ b/packages/graphql/tests/integration/directives/vector/vector-auth.int.test.ts @@ -478,7 +478,7 @@ describe("@vector directive - Auth", () => { const query = /* GraphQL */ ` query($vector: [Float!]) { - ${queryName}(vector: $vector, where: { node: { name: "${person2.name}" } }) { + ${queryName}(vector: $vector, where: { node: { name_EQ: "${person2.name}" } }) { edges { score node { diff --git a/packages/graphql/tests/integration/directives/vector/vector-limit-required.int.test.ts b/packages/graphql/tests/integration/directives/vector/vector-limit-required.int.test.ts new file mode 100644 index 0000000000..60fb658202 --- /dev/null +++ b/packages/graphql/tests/integration/directives/vector/vector-limit-required.int.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { offsetToCursor } from "graphql-relay"; +import { type Driver } from "neo4j-driver"; +import { generate } from "randomstring"; +import type { Neo4jGraphQL } from "../../../../src/classes"; +import type { UniqueType } from "../../../utils/graphql-types"; +import { isMultiDbUnsupportedError } from "../../../utils/is-multi-db-unsupported-error"; +import { TestHelper } from "../../../utils/tests-helper"; +import { testVectors } from "./shared-vector"; +import { GraphQLError } from "graphql"; + +describe("@vector directive - limit required", () => { + const queryName = "myQueryName"; + const testHelper = new TestHelper(); + + let driver: Driver; + let MULTIDB_SUPPORT = true; + let VECTOR_SUPPORT = true; + let neoSchema: Neo4jGraphQL; + + let Movie: UniqueType; + + const movie1 = { + title: "Some Title", + released: 2001, + embedding: testVectors[0], + }; + const movie2 = { + title: "Another Title", + released: 2002, + embedding: testVectors[1], + }; + const movie3 = { + title: "Another Title: The revenge", + released: 2002, + embedding: testVectors[2], + }; + + beforeAll(async () => { + const dbInfo = await testHelper.getDatabaseInfo(); + // No vector support, so we skip tests + if (!dbInfo.gte("5.15")) { + VECTOR_SUPPORT = false; + await testHelper.close(); + return; + } + + const databaseName = generate({ readable: true, charset: "alphabetic" }); + + try { + await testHelper.createDatabase(databaseName); + driver = await testHelper.getDriver(); + } catch (e) { + if (e instanceof Error) { + if (isMultiDbUnsupportedError(e)) { + // No multi-db support, so we skip tests + MULTIDB_SUPPORT = false; + await testHelper.close(); + return; + } else { + throw e; + } + } + } + + Movie = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + type ${Movie.name} @vector(indexes: [{ indexName: "${Movie}Index", embeddingProperty: "embedding", queryName: "${queryName}" }]) @node { + title: String! + released: Int! + }`; + + neoSchema = await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + limitRequired: true, + }, + }); + await neoSchema.getSchema(); + + await testHelper.executeCypher( + `CREATE VECTOR INDEX ${Movie.name}Index + IF NOT EXISTS FOR (n:${Movie.name}) + ON n.embedding + OPTIONS { + indexConfig: { + \`vector.dimensions\`: 128, + \`vector.similarity_function\`: 'cosine' + } + } + ` + ); + + await testHelper.executeCypher( + ` + CREATE (movie1:${Movie}) + CREATE (movie2:${Movie}) + CREATE (movie3:${Movie}) + SET movie1 = $movie1 + SET movie2 = $movie2 + SET movie3 = $movie3 + `, + { movie1, movie2, movie3 } + ); + + await neoSchema.assertIndexesAndConstraints({ + driver, + sessionConfig: { database: databaseName }, + }); + }); + + afterAll(async () => { + if (MULTIDB_SUPPORT && VECTOR_SUPPORT) { + await testHelper.dropDatabase(); + await testHelper.close(); + } + }); + + test("pagination with vector", async () => { + // Skip if vector not supported + if (!VECTOR_SUPPORT) { + console.log("VECTOR SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + // Skip if multi-db not supported + if (!MULTIDB_SUPPORT) { + console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + // skips first elements + const cursor = offsetToCursor(0); + const query = ` + query($vector: [Float!]) { + ${queryName}(vector: $vector, first: 2, after: "${cursor}") { + edges { + score + node { + title + } + } + } + } + `; + const gqlResult = await testHelper.executeGraphQL(query, { variableValues: { vector: testVectors[0] } }); + + expect(gqlResult.errors).toBeFalsy(); + expect(gqlResult.data).toEqual({ + [queryName]: { + edges: [ + { + node: { + title: "Another Title", + }, + score: expect.closeTo(0.56), + }, + { + node: { + title: "Another Title: The revenge", + }, + score: expect.closeTo(0.48), + }, + ], + }, + }); + }); + test("error limit not provided", async () => { + // Skip if vector not supported + if (!VECTOR_SUPPORT) { + console.log("VECTOR SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + // Skip if multi-db not supported + if (!MULTIDB_SUPPORT) { + console.log("MULTIDB_SUPPORT NOT AVAILABLE - SKIPPING"); + return; + } + + // skips first elements + const cursor = offsetToCursor(0); + const query = ` + query($vector: [Float!]) { + ${queryName}(vector: $vector, after: "${cursor}") { + edges { + score + node { + title + } + } + } + } + `; + const gqlResult = await testHelper.executeGraphQL(query, { variableValues: { vector: testVectors[0] } }); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${queryName}" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); +}); diff --git a/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts b/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts index 7906479bd0..6de3c15c69 100644 --- a/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts +++ b/packages/graphql/tests/integration/filtering/advanced-filtering.int.test.ts @@ -32,11 +32,11 @@ describe("Advanced Filtering", () => { test("should find Movies IN strings", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -57,18 +57,18 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - `, + CREATE (:${randomType.name} {property: $value}) + `, { value } ); - const query = ` - { - ${randomType.plural}(where: { property_IN: ["${value}", "${randomValue1}", "${randomValue2}"] }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { in: ["${value}", "${randomValue1}", "${randomValue2}"]} }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -80,11 +80,11 @@ describe("Advanced Filtering", () => { test("should find Movies REGEX", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs, @@ -104,18 +104,18 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - `, + CREATE (:${randomType.name} {property: $value}) + `, { value: `${value}${value}` } ); - const query = ` - { - ${randomType.plural}(where: { property_MATCHES: "(?i)${value}.*" }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { matches: "(?i)${value}.*" } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -127,11 +127,11 @@ describe("Advanced Filtering", () => { test("should find Movies NOT string", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -147,19 +147,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $randomValue1}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $randomValue1}) + `, { value, randomValue1 } ); - const query = ` - { - ${randomType.plural}(where: { NOT: { property_EQ: "${randomValue1}" } }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { NOT: { property: { eq: "${randomValue1}" } } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -173,11 +173,11 @@ describe("Advanced Filtering", () => { test("should find Movies CONTAINS string", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -190,20 +190,20 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $superValue}) - CREATE (:${randomType.name} {property: $superValue}) - CREATE (:${randomType.name} {property: $superValue}) - `, + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, { superValue } ); - const query = ` - { - ${randomType.plural}(where: { property_CONTAINS: "${value}" }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { contains: "${value}" } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -217,11 +217,11 @@ describe("Advanced Filtering", () => { test("should find Movies STARTS_WITH string", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -234,20 +234,20 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $superValue}) - CREATE (:${randomType.name} {property: $superValue}) - CREATE (:${randomType.name} {property: $superValue}) - `, + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, { superValue } ); - const query = ` - { - ${randomType.plural}(where: { property_STARTS_WITH: "${value}" }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { startsWith: "${value}" } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -263,11 +263,11 @@ describe("Advanced Filtering", () => { test("should find Movies ENDS_WITH string", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -285,20 +285,20 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $notValue}) - CREATE (:${randomType.name} {property: $superValue}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $notValue}) + CREATE (:${randomType.name} {property: $superValue}) + `, { value, notValue, superValue } ); - const query = ` - { - ${randomType.plural}(where: { property_ENDS_WITH: "${value}" }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { endsWith: "${value}" } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -312,11 +312,11 @@ describe("Advanced Filtering", () => { test("should find Movies implicit EQ string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ typeDefs, @@ -329,21 +329,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $animatrix}) - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - `, + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, { animatrix, matrix, matrixReloaded, matrixRevolutions } ); - const query = ` - { - ${movieType.plural}(where: { title_EQ: "${matrix}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { eq: "${matrix}" }}) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -360,11 +360,11 @@ describe("Advanced Filtering", () => { test("should find Movies EQ string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ typeDefs, @@ -377,21 +377,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $animatrix}) - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - `, + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, { animatrix, matrix, matrixReloaded, matrixRevolutions } ); - const query = ` - { - ${movieType.plural}(where: { title_EQ: "${matrix}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { eq: "${matrix}" } }) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -408,11 +408,11 @@ describe("Advanced Filtering", () => { test("should find Movies GT string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ features: { @@ -435,21 +435,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $animatrix}) - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - `, + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, { animatrix, matrix, matrixReloaded, matrixRevolutions } ); - const query = ` - { - ${movieType.plural}(where: { title_GT: "${matrix}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { gt: "${matrix}" } }) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -468,11 +468,11 @@ describe("Advanced Filtering", () => { test("should find Movies LT string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ features: { @@ -495,21 +495,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - CREATE (:${movieType.name} {title: $matrixResurrections}) - `, + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + CREATE (:${movieType.name} {title: $matrixResurrections}) + `, { matrix, matrixReloaded, matrixRevolutions, matrixResurrections } ); - const query = ` - { - ${movieType.plural}(where: { title_LT: "${matrixRevolutions}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { lt: "${matrixRevolutions}" } }) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -528,11 +528,11 @@ describe("Advanced Filtering", () => { test("should find Movies GTE string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ features: { @@ -555,21 +555,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $animatrix}) - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - `, + CREATE (:${movieType.name} {title: $animatrix}) + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + `, { animatrix, matrix, matrixReloaded, matrixRevolutions } ); - const query = ` - { - ${movieType.plural}(where: { title_GTE: "${matrix}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { gte: "${matrix}" } }) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -588,11 +588,11 @@ describe("Advanced Filtering", () => { test("should find Movies LTE string", async () => { const movieType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${movieType.name} @node { - title: String - } - `; + const typeDefs = /* GraphQL */ ` + type ${movieType.name} @node { + title: String + } + `; await testHelper.initNeo4jGraphQL({ features: { @@ -615,22 +615,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${movieType.name} {title: $matrix}) - CREATE (:${movieType.name} {title: $matrixReloaded}) - CREATE (:${movieType.name} {title: $matrixRevolutions}) - CREATE (:${movieType.name} {title: $matrixResurrections}) - - `, + CREATE (:${movieType.name} {title: $matrix}) + CREATE (:${movieType.name} {title: $matrixReloaded}) + CREATE (:${movieType.name} {title: $matrixRevolutions}) + CREATE (:${movieType.name} {title: $matrixResurrections}) + `, { matrix, matrixReloaded, matrixRevolutions, matrixResurrections } ); - const query = ` - { - ${movieType.plural}(where: { title_LTE: "${matrixRevolutions}" }) { - title - } - } - `; + const query = /* GraphQL */ ` + { + ${movieType.plural}(where: { title: { lte: "${matrixRevolutions}" } }) { + title + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -656,11 +655,11 @@ describe("Advanced Filtering", () => { test("should find Movies NOT number", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -682,19 +681,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $property}) - CREATE (:${randomType.name} {property: $notProperty}) - `, + CREATE (:${randomType.name} {property: $property}) + CREATE (:${randomType.name} {property: $notProperty}) + `, { property, notProperty } ); - const query = ` - { - ${randomType.plural}(where: { NOT: { property_EQ: ${notProperty} } }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { NOT: { property: { eq: ${notProperty} }} }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -707,11 +706,11 @@ describe("Advanced Filtering", () => { test("should find Movies IN numbers", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -741,18 +740,18 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - `, + CREATE (:${randomType.name} {property: $value}) + `, { value } ); - const query = ` - { - ${randomType.plural}(where: { property_IN: [${value}, ${randomValue1}, ${randomValue2}] }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { in: [${value}, ${randomValue1}, ${randomValue2}] } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -765,11 +764,11 @@ describe("Advanced Filtering", () => { test("should find Movies LT number", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -785,19 +784,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $lessThanValue}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $lessThanValue}) + `, { value, lessThanValue } ); - const query = ` - { - ${randomType.plural}(where: { property_LT: ${lessThanValue + 1} }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: {lt: ${lessThanValue + 1}} }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -810,11 +809,11 @@ describe("Advanced Filtering", () => { test("should find Movies LTE number", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -830,19 +829,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $lessThanValue}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $lessThanValue}) + `, { value, lessThanValue } ); - const query = ` - { - ${randomType.plural}(where: { property_LTE: ${value} }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { lte: ${value} } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -854,11 +853,11 @@ describe("Advanced Filtering", () => { test("should find Movies GT number", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -874,19 +873,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $graterThanValue}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $graterThanValue}) + `, { value, graterThanValue } ); - const query = ` - { - ${randomType.plural}(where: { property_GT: ${graterThanValue - 1} }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { gt: ${graterThanValue - 1} } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -899,11 +898,11 @@ describe("Advanced Filtering", () => { test("should find Movies GTE number", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: ${type} - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: ${type} + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -919,19 +918,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - CREATE (:${randomType.name} {property: $greaterThan}) - `, + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $greaterThan}) + `, { value, greaterThan } ); - const query = ` - { - ${randomType.plural}(where: { property_GTE: ${value} }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { gte: ${value} } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -945,11 +944,11 @@ describe("Advanced Filtering", () => { test("should find Movies equality equality", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: Boolean - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: Boolean + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -957,18 +956,18 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - `, + CREATE (:${randomType.name} {property: $value}) + `, { value } ); - const query = ` - { - ${randomType.plural}(where: { property_EQ: false }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { property: { eq: false } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -980,11 +979,11 @@ describe("Advanced Filtering", () => { test("should find Movies NOT boolean", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` - type ${randomType.name} @node { - property: Boolean - } - `; + const typeDefs = /* GraphQL */ ` + type ${randomType.name} @node { + property: Boolean + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -992,18 +991,18 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {property: $value}) - `, + CREATE (:${randomType.name} {property: $value}) + `, { value } ); - const query = ` - { - ${randomType.plural}(where: { NOT: { property_EQ: false } }) { - property - } - } - `; + const query = /* GraphQL */ ` + { + ${randomType.plural}(where: { NOT: { property: { eq: false } } }) { + property + } + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -1019,7 +1018,7 @@ describe("Advanced Filtering", () => { const randomType1 = testHelper.createUniqueType("Movie"); const randomType2 = testHelper.createUniqueType("Genre"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${randomType1.name} @node { id: ID ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) @@ -1046,25 +1045,25 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (root:${randomType1.name} {id: $rootId}) - CREATE (:${randomType1.name} {id: $randomId}) - CREATE (relation:${randomType2.name} {id: $relationId}) - CREATE (:${randomType2.name} {id: $randomId}) - MERGE (root)-[:IN_GENRE]->(relation) - `, + CREATE (root:${randomType1.name} {id: $rootId}) + CREATE (:${randomType1.name} {id: $randomId}) + CREATE (relation:${randomType2.name} {id: $relationId}) + CREATE (:${randomType2.name} {id: $randomId}) + MERGE (root)-[:IN_GENRE]->(relation) + `, { rootId, relationId, randomId } ); - const query = ` - { - ${randomType1.plural}(where: { ${randomType2.plural}_SOME: { id_EQ: "${relationId}" } }) { + const query = /* GraphQL */ ` + { + ${randomType1.plural}(where: { ${randomType2.plural}: { some: { id: { eq: "${relationId}" }} } }) { + id + ${randomType2.plural} { id - ${randomType2.plural} { - id - } } } - `; + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -1081,7 +1080,7 @@ describe("Advanced Filtering", () => { const Movie = testHelper.createUniqueType("Movie"); const Genre = testHelper.createUniqueType("Genre"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT) @@ -1104,21 +1103,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${Movie} {id: $movieId})-[:IN_GENRE]->(:${Genre} {id:$genreId}) - `, + CREATE (:${Movie} {id: $movieId})-[:IN_GENRE]->(:${Genre} {id:$genreId}) + `, { movieId, genreId } ); - const query = ` - { - ${Movie.plural}(where: { genresConnection_SOME: { node: { id_EQ: "${genreId}" } } }) { + const query = /* GraphQL */ ` + { + ${Movie.plural}(where: { genresConnection: { some: { node: { id: { eq: "${genreId}" } } } } }) { + id + genres { id - genres { - id - } } } - `; + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -1138,19 +1137,19 @@ describe("Advanced Filtering", () => { const Movie = testHelper.createUniqueType("Movie"); const Genre = testHelper.createUniqueType("Genre"); - const typeDefs = ` - type ${Movie} @node { - id: ID - genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") - } + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID + genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") + } - type ${Genre} @node { - id: ID - } + type ${Genre} @node { + id: ID + } - type ActedIn @relationshipProperties { - id: String - } + type ActedIn @relationshipProperties { + id: String + } `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -1169,21 +1168,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (movie:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) - `, + CREATE (movie:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) + `, { movieId, genreId, actedInId } ); - const query = ` - { - ${Movie.plural}(where: { genresConnection_SOME: { edge: { id_EQ: "${actedInId}" } } }) { + const query = /* GraphQL */ ` + { + ${Movie.plural}(where: { genresConnection: { some: { edge: { id: { eq: "${actedInId}" } } } } }) { + id + genres { id - genres { - id - } } } - `; + } + `; const gqlResult = await testHelper.executeGraphQL(query); @@ -1202,7 +1201,7 @@ describe("Advanced Filtering", () => { const Movie = testHelper.createUniqueType("Movie"); const Genre = testHelper.createUniqueType("Genre"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID genres: [${Genre}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") @@ -1233,14 +1232,14 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) - `, + CREATE (:${Movie} {id: $movieId})-[:IN_GENRE {id:$actedInId}]->(:${Genre} {id:$genreId}) + `, { movieId, genreId, actedInId } ); - const query = ` + const query = /* GraphQL */ ` { - ${Movie.plural}(where: { genresConnection_SOME: { node: { id_EQ: "${genreId}" } edge: { id_EQ: "${actedInId}" } } }) { + ${Movie.plural}(where: { genresConnection: { some: { node: { id: { eq: "${genreId}" }} edge: { id: { eq: "${actedInId}" } } } } }) { id genres { id @@ -1269,15 +1268,15 @@ describe("Advanced Filtering", () => { const randomType1 = testHelper.createUniqueType("Movie"); const randomType2 = testHelper.createUniqueType("Genre"); - const typeDefs = ` - type ${randomType1.name} @node { - id: ID - ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } + const typeDefs = /* GraphQL */ ` + type ${randomType1.name} @node { + id: ID + ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) + } - type ${randomType2.name} @node { - id: ID - } + type ${randomType2.name} @node { + id: ID + } `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -1298,19 +1297,19 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (root1:${randomType1.name} {id: $rootId1}) - CREATE (root2:${randomType1.name} {id: $rootId2}) - CREATE (relation1:${randomType2.name} {id: $relationId1}) - CREATE (relation2:${randomType2.name} {id: $relationId2}) - MERGE (root1)-[:IN_GENRE]->(relation1) - MERGE (root2)-[:IN_GENRE]->(relation2) - `, + CREATE (root1:${randomType1.name} {id: $rootId1}) + CREATE (root2:${randomType1.name} {id: $rootId2}) + CREATE (relation1:${randomType2.name} {id: $relationId1}) + CREATE (relation2:${randomType2.name} {id: $relationId2}) + MERGE (root1)-[:IN_GENRE]->(relation1) + MERGE (root2)-[:IN_GENRE]->(relation2) + `, { rootId1, rootId2, relationId1, relationId2 } ); const query = /* GraphQL */ ` { - ${randomType1.plural}(where: { NOT: { ${randomType2.plural}_SOME: { id_EQ: "${relationId2}" } } }) { + ${randomType1.plural}(where: { NOT: { ${randomType2.plural}: { some: { id: { eq: "${relationId2}" }}} } }) { id ${randomType2.plural} { id @@ -1334,19 +1333,19 @@ describe("Advanced Filtering", () => { const randomType1 = testHelper.createUniqueType("Movie"); const randomType2 = testHelper.createUniqueType("Genre"); - const typeDefs = ` - type ${randomType1.name} @node { - id: ID - ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") - } + const typeDefs = /* GraphQL */ ` + type ${randomType1.name} @node { + id: ID + ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT, properties: "ActedIn") + } - type ${randomType2.name} @node { - id: ID - } + type ${randomType2.name} @node { + id: ID + } - type ActedIn @relationshipProperties { - id: ID - } + type ActedIn @relationshipProperties { + id: ID + } `; await testHelper.initNeo4jGraphQL({ typeDefs }); @@ -1370,15 +1369,15 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType1.name} {id: $rootId1})-[:IN_GENRE {id: $actedInId}]->(:${randomType2.name} {id: $relationId1}) - CREATE (:${randomType1.name} {id: $rootId2})-[:IN_GENRE {id: randomUUID()}]->(:${randomType2.name} {id: $relationId2}) - `, + CREATE (:${randomType1.name} {id: $rootId1})-[:IN_GENRE {id: $actedInId}]->(:${randomType2.name} {id: $relationId1}) + CREATE (:${randomType1.name} {id: $rootId2})-[:IN_GENRE {id: randomUUID()}]->(:${randomType2.name} {id: $relationId2}) + `, { rootId1, rootId2, relationId1, relationId2, actedInId } ); - const query = ` + const query = /* GraphQL */ ` { - ${randomType1.plural}(where: { ${randomType2.plural}Connection_NONE: { edge: { id_EQ: "${actedInId}" } } }) { + ${randomType1.plural}(where: { ${randomType2.plural}Connection: { none: {edge: { id: { eq: "${actedInId}"} } } } }) { id ${randomType2.plural} { id @@ -1418,19 +1417,19 @@ describe("Advanced Filtering", () => { Movie = testHelper.createUniqueType("Movie"); Actor = testHelper.createUniqueType("Actor"); - const typeDefs = ` - type ${Movie} @node { - id: ID! @id @unique - budget: Int! - actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) - } + const typeDefs = /* GraphQL */ ` + type ${Movie} @node { + id: ID! @id + budget: Int! + actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: IN) + } - type ${Actor} @node { - id: ID! @id @unique - flag: Boolean! - actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; + type ${Actor} @node { + id: ID! @id + flag: Boolean! + actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) + } + `; await testHelper.initNeo4jGraphQL({ typeDefs }); await testHelper.executeCypher( @@ -1454,12 +1453,12 @@ describe("Advanced Filtering", () => { }); describe("on relationship", () => { - function generateQuery(predicate: "ALL" | "NONE" | "SINGLE" | "SOME") { + function generateQuery(predicate: "all" | "none" | "single" | "some") { return /* GraphQL */ ` query($movieIds: [ID!]!) { - ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actors_${predicate}: { NOT: { flag_EQ: false } } }] }) { + ${Movie.plural}(where: { AND: [{ id: { in: $movieIds }}, { actors: { ${predicate}: { NOT: { flag: { eq: false }} } } }] }) { id - actors(where: { NOT: { flag_EQ: false } }) { + actors(where: { NOT: { flag: {eq: false} } }) { id flag } @@ -1468,8 +1467,8 @@ describe("Advanced Filtering", () => { `; } - test("ALL", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + test("all", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("all"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1484,8 +1483,8 @@ describe("Advanced Filtering", () => { }); }); - test("NONE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + test("none", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("none"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1500,8 +1499,8 @@ describe("Advanced Filtering", () => { }); }); - test("SINGLE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + test("single", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("single"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1516,8 +1515,8 @@ describe("Advanced Filtering", () => { }); }); - test("SOME", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + test("some", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("some"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1542,11 +1541,11 @@ describe("Advanced Filtering", () => { }); describe("on relationship using NOT operator", () => { - const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => ` + const generateQuery = (predicate: "all" | "none" | "single" | "some") => /* GraphQL */ ` query($movieIds: [ID!]!) { - ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actors_${predicate}: { NOT: { flag_EQ: false } } }] }) { + ${Movie.plural}(where: { AND: [{ id: { in: $movieIds }}, { actors: { ${predicate}: { NOT: { flag: {eq: false }}} } }] }) { id - actors(where: { NOT: { flag_EQ: false } }) { + actors(where: { NOT: { flag: { eq: false }} }) { id flag } @@ -1554,8 +1553,8 @@ describe("Advanced Filtering", () => { } `; - test("ALL", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + test("all", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("all"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1570,8 +1569,8 @@ describe("Advanced Filtering", () => { }); }); - test("NONE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + test("none", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("none"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1586,8 +1585,8 @@ describe("Advanced Filtering", () => { }); }); - test("SINGLE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + test("single", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("single"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1602,8 +1601,8 @@ describe("Advanced Filtering", () => { }); }); - test("SOME", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + test("some", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("some"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1628,11 +1627,11 @@ describe("Advanced Filtering", () => { }); describe("on connection", () => { - const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => /* GraphQL */ ` + const generateQuery = (predicate: "all" | "none" | "single" | "some") => /* GraphQL */ ` query($movieIds: [ID!]!) { - ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actorsConnection_${predicate}: { node: { NOT: { flag_EQ: false } } }}] }) { + ${Movie.plural}(where: { AND: [{ id: {in: $movieIds} }, { actorsConnection: { ${predicate}: { node: { NOT: { flag: { eq: false} } } }}}] }) { id - actors(where: { NOT: { flag_EQ: false } }) { + actors(where: { NOT: { flag:{ eq: false }} }) { id flag } @@ -1640,8 +1639,8 @@ describe("Advanced Filtering", () => { } `; - test("ALL", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + test("all", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("all"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1656,8 +1655,8 @@ describe("Advanced Filtering", () => { }); }); - test("NONE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + test("none", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("none"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1672,8 +1671,8 @@ describe("Advanced Filtering", () => { }); }); - test("SINGLE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + test("single", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("single"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1688,8 +1687,8 @@ describe("Advanced Filtering", () => { }); }); - test("SOME", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + test("some", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("some"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1714,11 +1713,11 @@ describe("Advanced Filtering", () => { }); describe("on connection using NOT operator", () => { - const generateQuery = (predicate: "ALL" | "NONE" | "SINGLE" | "SOME") => ` + const generateQuery = (predicate: "all" | "none" | "single" | "some") => /* GraphQL */ ` query($movieIds: [ID!]!) { - ${Movie.plural}(where: { AND: [{ id_IN: $movieIds }, { actorsConnection_${predicate}: { node: { NOT: { flag_EQ: false } } } }] }) { + ${Movie.plural}(where: { AND: [{ id:{ in: $movieIds }}, { actorsConnection: { ${predicate}: { node: { NOT: { flag: {eq: false} } } } } }] }) { id - actors(where: { NOT: { flag_EQ: false }}) { + actors(where: { NOT: { flag: { eq: false } }}) { id flag } @@ -1726,8 +1725,8 @@ describe("Advanced Filtering", () => { } `; - test("ALL", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("ALL"), { + test("all", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("all"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1742,8 +1741,8 @@ describe("Advanced Filtering", () => { }); }); - test("NONE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("NONE"), { + test("none", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("none"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1758,8 +1757,8 @@ describe("Advanced Filtering", () => { }); }); - test("SINGLE", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SINGLE"), { + test("single", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("single"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1774,8 +1773,8 @@ describe("Advanced Filtering", () => { }); }); - test("SOME", async () => { - const gqlResult = await testHelper.executeGraphQL(generateQuery("SOME"), { + test("some", async () => { + const gqlResult = await testHelper.executeGraphQL(generateQuery("some"), { variableValues: { movieIds: movies.map(({ id }) => id) }, }); @@ -1799,66 +1798,6 @@ describe("Advanced Filtering", () => { }); }); }); - - test("should test for not null", async () => { - const randomType1 = testHelper.createUniqueType("Movie"); - const randomType2 = testHelper.createUniqueType("Genre"); - - const typeDefs = ` - type ${randomType1.name} @node { - id: ID - ${randomType2.plural}: [${randomType2.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${randomType2.name} @node { - id: ID - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const rootId = generate({ - charset: "alphabetic", - }); - - const relationId = generate({ - charset: "alphabetic", - }); - - const randomId = generate({ - charset: "alphabetic", - }); - - await testHelper.executeCypher( - ` - CREATE (root:${randomType1.name} {id: $rootId}) - CREATE (:${randomType1.name} {id: $randomId}) - CREATE (relation:${randomType2.name} {id: $relationId}) - CREATE (:${randomType2.name} {id: $randomId}) - MERGE (root)-[:IN_GENRE]->(relation) - `, - { rootId, relationId, randomId } - ); - - const nullQuery = ` - { - ${randomType1.plural}(where: { ${randomType2.plural}_SOME: null }) { - id - } - } - `; - - // Test null checking (nodes without any related nodes on the specified field) - - const nullResult = await testHelper.executeGraphQL(nullQuery); - - expect(nullResult.errors).toBeUndefined(); - - expect((nullResult.data as any)[randomType1.plural]).toHaveLength(1); - expect((nullResult.data as any)[randomType1.plural][0]).toMatchObject({ - id: randomId, - }); - }); }); describe("NULL Filtering", () => { @@ -1866,7 +1805,7 @@ describe("Advanced Filtering", () => { test("should work for existence and non-existence", async () => { const randomType = testHelper.createUniqueType("Movie"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${randomType.name} @node { id: String! optional: String @@ -1892,21 +1831,21 @@ describe("Advanced Filtering", () => { await testHelper.executeCypher( ` - CREATE (:${randomType.name} {id: $id1}) - CREATE (:${randomType.name} {id: $id2, optional: $optionalValue}) - `, + CREATE (:${randomType.name} {id: $id1}) + CREATE (:${randomType.name} {id: $id2, optional: $optionalValue}) + `, { id1, id2, optionalValue } ); // Test NULL checking - const nullQuery = ` - { - ${randomType.plural}(where: { optional_EQ: null }) { - id - } + const nullQuery = /* GraphQL */ ` + { + ${randomType.plural}(where: { optional: { eq: null} }) { + id } - `; + } + `; const nullResult = await testHelper.executeGraphQL(nullQuery); @@ -1918,13 +1857,13 @@ describe("Advanced Filtering", () => { // Test NOT NULL checking - const notNullQuery = ` - { - ${randomType.plural}(where: { NOT: { optional_EQ: null } }) { - id - } + const notNullQuery = /* GraphQL */ ` + { + ${randomType.plural}(where: { NOT: { optional: { eq: null } } }) { + id } - `; + } + `; const notNullResult = await testHelper.executeGraphQL(notNullQuery); diff --git a/packages/graphql/tests/integration/filtering/custom-scalar-filtering.int.test.ts b/packages/graphql/tests/integration/filtering/custom-scalar-filtering.int.test.ts new file mode 100644 index 0000000000..e7d92f1f41 --- /dev/null +++ b/packages/graphql/tests/integration/filtering/custom-scalar-filtering.int.test.ts @@ -0,0 +1,526 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestHelper } from "../../utils/tests-helper"; + +describe("Custom Scalar Filtering", () => { + const testHelper = new TestHelper(); + + afterEach(async () => { + await testHelper.close(); + }); + + describe("Single Value Custom Scalar", () => { + test("Filter NOT CustomScalar - expect return value", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = 1; + const unwantedValue = 2; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value, unwantedValue } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property: {eq: ${unwantedValue} } } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + + test("Filter NOT CustomScalar - expect array of return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value1 = 1; + const value2 = 202; + const unwantedValue = 2; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value1}) + CREATE (:${randomType.name} {property: $value2}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value1, value2, unwantedValue } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property: {eq: ${unwantedValue} } } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + expect((gqlResult.data as any)[randomType.plural]).toIncludeSameMembers([ + { + property: value1, + }, + { + property: value2, + }, + ]); + }); + test("Filter NOT CustomScalar - expect no return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = 11; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { NOT: { property: { eq: ${value} } }}) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(0); + }); + test("Filter IN CustomScalar - expect return value", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = "someValue"; + const unknownValue1 = "foo"; + const unknownValue2 = "bar"; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { + property: {in: ["${value}", "${unknownValue1}", "${unknownValue2}"] } + }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + test("Filter IN CustomScalar - expect array of return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value1 = "someValue"; + const value2 = "someOtherValue"; + const unwantedValue = "foo"; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value1}) + CREATE (:${randomType.name} {property: $value2}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value1, value2, unwantedValue } + ); + + const query = ` + { + ${randomType.plural}(where: { + property: {in: ["${value1}", "${value2}"] } + }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + expect((gqlResult.data as any)[randomType.plural]).toIncludeSameMembers([ + { + property: value1, + }, + { + property: value2, + }, + ]); + }); + test("Filter IN CustomScalar - expect no return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomScalar + + type ${randomType.name} @node { + property: CustomScalar + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = "someValue"; + const unknownValue = "someUnknownValue"; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { property: { in: ["${unknownValue}"] } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(0); + }); + }); + describe("List Custom Scalar Filtering", () => { + test("Filter NOT CustomListScalar - expect return value", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = [1, 2, 3]; + const unwantedValue = [2, 3]; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value, unwantedValue } + ); + + const query = ` + query($unwantedValue: [CustomListScalar!]!){ + ${randomType.plural}(where: { NOT: { property: { eq: $unwantedValue } } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { unwantedValue }, + }); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + test("Filter NOT CustomListScalar - expect array of return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value1 = [1, 2, 3]; + const value2 = ["someValue"]; + const unwantedValue = [2, 3]; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value1}) + CREATE (:${randomType.name} {property: $value2}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value1, value2, unwantedValue } + ); + + const query = ` + query($unwantedValue: [CustomListScalar!]!){ + ${randomType.plural}(where: { NOT: { property: {eq: $unwantedValue } }}) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { unwantedValue }, + }); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + expect((gqlResult.data as any)[randomType.plural]).toIncludeSameMembers([ + { + property: value1, + }, + { + property: value2, + }, + ]); + }); + test("Filter NOT CustomListScalar - expect no return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = [1, 2, 3]; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + query($value: [CustomListScalar!]!){ + ${randomType.plural}(where: { NOT: { property: {eq: $value } } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { value }, + }); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(0); + }); + test("Filter INCLUDES CustomListScalar - expect return value", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = ["val1", "val2", "val3"]; + const unwantedValue = ["foo", "bar"]; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value, unwantedValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property: { includes: ${value[0]} } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(1); + expect((gqlResult.data as any)[randomType.plural][0].property).toEqual(value); + }); + test("Filter INCLUDES CustomListScalar - expect array of return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value1 = ["val1", "val2", "val3"]; + const value2 = [value1[0]]; + const unwantedValue = ["foo", "bar"]; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value1}) + CREATE (:${randomType.name} {property: $value2}) + CREATE (:${randomType.name} {property: $unwantedValue}) + `, + { value1, value2, unwantedValue } + ); + + const query = ` + { + ${randomType.plural}(where: { property: {includes: ${value1[0]} } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(2); + expect((gqlResult.data as any)[randomType.plural]).toIncludeSameMembers([ + { + property: value1, + }, + { + property: value2, + }, + ]); + }); + test("Filter INCLUDES CustomListScalar - expect no return values", async () => { + const randomType = testHelper.createUniqueType("Movie"); + + const typeDefs = ` + scalar CustomListScalar + + type ${randomType.name} @node { + property: [CustomListScalar!] + } + `; + + await testHelper.initNeo4jGraphQL({ typeDefs }); + + const value = ["val1", "val2", "val3"]; + const unknownValue = "foo"; + + await testHelper.executeCypher( + ` + CREATE (:${randomType.name} {property: $value}) + `, + { value } + ); + + const query = ` + { + ${randomType.plural}(where: { property: {includes: ${unknownValue[0]} } }) { + property + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[randomType.plural]).toHaveLength(0); + }); + }); +}); diff --git a/packages/graphql/tests/integration/issues/5698.int.test.ts b/packages/graphql/tests/integration/filtering/enum.int.test.ts similarity index 55% rename from packages/graphql/tests/integration/issues/5698.int.test.ts rename to packages/graphql/tests/integration/filtering/enum.int.test.ts index 6861db05f1..c14f7aa12e 100644 --- a/packages/graphql/tests/integration/issues/5698.int.test.ts +++ b/packages/graphql/tests/integration/filtering/enum.int.test.ts @@ -20,27 +20,24 @@ import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; -describe("https://github.com/neo4j/graphql/issues/5698", () => { +describe("enum filtering", () => { let Movie: UniqueType; - let Person: UniqueType; const testHelper = new TestHelper(); beforeEach(async () => { Movie = testHelper.createUniqueType("Movie"); - Person = testHelper.createUniqueType("Person"); const typeDefs = /* GraphQL */ ` - type ${Movie} { - title: String - released: Int - director: ${Person}! @relationship(type: "DIRECTED", direction: IN) - secondDirector: ${Person} @relationship(type: "ALSO_DIRECTED", direction: IN) + enum Genre { + THRILLER, + COMEDY + SCIFI } - type ${Person} { - name: String - directed: [${Movie}!]! @relationship(type: "DIRECTED", direction: OUT) + type ${Movie} @node { + title: String! + genre: Genre! } `; await testHelper.initNeo4jGraphQL({ @@ -52,14 +49,13 @@ describe("https://github.com/neo4j/graphql/issues/5698", () => { await testHelper.close(); }); - test("should not return non-null 1-1 relationships when passing null", async () => { - await testHelper.executeCypher(` - CREATE (:${Movie} {title: "The Matrix", released: 1999})<-[:DIRECTED]-(:${Person} {name: "Keanu"}) - `); + test("filter by eq", async () => { + await testHelper.executeCypher(`CREATE (:${Movie} {title: "The Matrix", genre: "SCIFI"}) + CREATE(:${Movie} {title: "Johnny English", genre: "COMEDY"})`); const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { released: 1999, director: null }) { + ${Movie.plural}(where: {genre: {eq: COMEDY}}) { title } } @@ -67,19 +63,18 @@ describe("https://github.com/neo4j/graphql/issues/5698", () => { const result = await testHelper.executeGraphQL(query); expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [Movie.plural]: [], + expect(result).toEqual({ + data: { [Movie.plural]: [{ title: "Johnny English" }] }, }); }); - test("should not return nullable 1-1 relationships when passing null", async () => { - await testHelper.executeCypher(` - CREATE (:${Movie} {title: "The Matrix", released: 1999})<-[:ALSO_DIRECTED]-(:${Person} {name: "Keanu"}) - `); + test("filter by not eq", async () => { + await testHelper.executeCypher(`CREATE (:${Movie} {title: "The Matrix", genre: "SCIFI"}) + CREATE(:${Movie} {title: "Johnny English", genre: "COMEDY"})`); const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { released: 1999, secondDirector: null }) { + ${Movie.plural}(where: {NOT: {genre: {eq: COMEDY}}}) { title } } @@ -87,8 +82,8 @@ describe("https://github.com/neo4j/graphql/issues/5698", () => { const result = await testHelper.executeGraphQL(query); expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [Movie.plural]: [], + expect(result).toEqual({ + data: { [Movie.plural]: [{ title: "The Matrix" }] }, }); }); }); diff --git a/packages/graphql/tests/integration/filtering/filter-interface-relationship-alias.int.test.ts b/packages/graphql/tests/integration/filtering/filter-interface-relationship-alias.int.test.ts index 6414c75896..a31e1de7db 100644 --- a/packages/graphql/tests/integration/filtering/filter-interface-relationship-alias.int.test.ts +++ b/packages/graphql/tests/integration/filtering/filter-interface-relationship-alias.int.test.ts @@ -58,7 +58,7 @@ describe("interface relationships aliased fields", () => { type ${typeActor} @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) + currentlyActingIn: [Production!]! @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } diff --git a/packages/graphql/tests/integration/filtering/filter-interface-relationship.int.test.ts b/packages/graphql/tests/integration/filtering/filter-interface-relationship.int.test.ts index 81a9124e64..2e5000a052 100644 --- a/packages/graphql/tests/integration/filtering/filter-interface-relationship.int.test.ts +++ b/packages/graphql/tests/integration/filtering/filter-interface-relationship.int.test.ts @@ -54,7 +54,7 @@ describe("interface relationships", () => { type ${typeActor} @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) + currentlyActingIn: [Production!]! @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; diff --git a/packages/graphql/tests/integration/filtering/filter-union-relationship.int.test.ts b/packages/graphql/tests/integration/filtering/filter-union-relationship.int.test.ts index ccd811b393..c6b86d966b 100644 --- a/packages/graphql/tests/integration/filtering/filter-union-relationship.int.test.ts +++ b/packages/graphql/tests/integration/filtering/filter-union-relationship.int.test.ts @@ -52,7 +52,7 @@ describe("union relationships", () => { type ${typeActor} @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) + currentlyActingIn: [Production!]! @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; diff --git a/packages/graphql/tests/integration/filtering/single-relationship.int.test.ts b/packages/graphql/tests/integration/filtering/single-relationship.int.test.ts deleted file mode 100644 index 19e2d73686..0000000000 --- a/packages/graphql/tests/integration/filtering/single-relationship.int.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Single relationship (1-*) filtering", () => { - let Person: UniqueType; - let Movie: UniqueType; - - const testHelper = new TestHelper(); - - beforeEach(async () => { - Person = testHelper.createUniqueType("Person"); - Movie = testHelper.createUniqueType("Movie"); - - const typeDefs = ` - type ${Person} @node { - name: String! - actedIn: [${Movie}!]! @relationship(type: "ACTED_IN", direction: OUT) - directedMovies: [${Movie}!]! @relationship(type: "DIRECTED", direction: OUT) - producedMovies: [${Movie}!]! @relationship(type: "PRODUCED", direction: OUT) - } - - type ${Movie} @node { - title: String! - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: OUT) - director: ${Person}! @relationship(type: "DIRECTED", direction: IN) - producer: ${Person} @relationship(type: "PRODUCED", direction: IN) - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {title: "The Matrix", released: 1999}) - CREATE (:${Movie} {title: "The Italian Job", released: 1969}) - CREATE (:${Movie} {title: "The Italian Job", released: 2003}) - CREATE (:${Movie} {title: "The Lion King", released: 1994}) - `); - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Filter on required and optional relationships", async () => { - const query = ` - query { - ${Movie.plural}(where: { OR: [{ director: { name_EQ: "Jon Wu" } }, { producer: { name_EQ: "Jon Wu" } }] }) { - title - } - } - `; - - await testHelper.executeCypher(` - CREATE(jw:${Person} {name: "Jon Wu"}) - CREATE(:${Movie} {title: "Hard Target"})<-[:DIRECTED]-(jw) - CREATE(cb:${Movie} {title: "Chi bi"})<-[:DIRECTED]-(jw) - CREATE(cb)<-[:PRODUCED]-(jw) - CREATE(m:${Movie} {title: "Avatar"})<-[:DIRECTED]-(:${Person} {name: "Richie McFamous"}) - `); - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeUndefined(); - - expect((result.data as any)[Movie.plural]).toIncludeSameMembers([ - { - title: "Chi bi", - }, - { title: "Hard Target" }, - ]); - }); - - it("Filter on required and optional relationships in nested queries", async () => { - const query = ` - query { - ${Person.plural}( - where: { actedIn_SOME: { OR: [{ director: { name_EQ: "Jon Wu" } }, { producer: { name_EQ: "Jon Wu" } }] } } - ) { - name - } - } - `; - - await testHelper.executeCypher(` - CREATE(a:${Person} {name: "That actor that you are not so sure what the name is but have seen before"}) - CREATE(a2:${Person} {name: "not so famous one"}) - CREATE(a3:${Person} {name: "don't know this one"}) - - - - CREATE(jw:${Person} {name: "Jon Wu"}) - CREATE(ht:${Movie} {title: "Hard Target"})<-[:DIRECTED]-(jw) - CREATE(cb:${Movie} {title: "Chi bi"})<-[:DIRECTED]-(jw) - CREATE(cb)<-[:PRODUCED]-(jw) - CREATE(m:${Movie} {title: "Avatar"})<-[:DIRECTED]-(:${Person} {name: "Richie McFamous"}) - - CREATE(a)-[:ACTED_IN]->(ht) - CREATE(a)-[:ACTED_IN]->(cb) - CREATE(a2)-[:ACTED_IN]->(cb) - CREATE(a)-[:ACTED_IN]->(m) - CREATE(a3)-[:ACTED_IN]->(m) - `); - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeUndefined(); - expect((result.data as any)[Person.plural]).toIncludeSameMembers([ - { - name: "That actor that you are not so sure what the name is but have seen before", - }, - { - name: "not so famous one", - }, - ]); - }); - - it("Filter on optional relationships without results", async () => { - const query = ` - query { - ${Movie.plural}(where: { producer: { name_EQ: "Uw Noj" } } ) { - title - } - } - `; - - await testHelper.executeCypher(` - CREATE(jw:${Person} {name: "Jon Wu"}) - CREATE(:${Movie} {title: "Hard Target"})<-[:DIRECTED]-(jw) - CREATE(cb:${Movie} {title: "Chi bi"})<-[:DIRECTED]-(jw) - CREATE(cb)<-[:PRODUCED]-(jw) - CREATE(m:${Movie} {title: "Avatar"})<-[:DIRECTED]-(:${Person} {name: "Richie McFamous"}) - `); - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeUndefined(); - - expect((result.data as any)[Movie.plural]).toBeEmpty(); - }); -}); diff --git a/packages/graphql/tests/integration/filtering/typename-in-auth.int.test.ts b/packages/graphql/tests/integration/filtering/typename-in-auth.int.test.ts index 2f265ba8c2..c8672e7eb6 100644 --- a/packages/graphql/tests/integration/filtering/typename-in-auth.int.test.ts +++ b/packages/graphql/tests/integration/filtering/typename-in-auth.int.test.ts @@ -94,7 +94,7 @@ describe("typename_IN with auth", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Movie.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Movie.name}] }] } } } } } @@ -142,7 +142,7 @@ describe("typename_IN with auth", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Series.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Series.name}] }] } } } } } @@ -185,7 +185,7 @@ describe("typename_IN with auth", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Movie.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Movie.name}] }] } } } } } @@ -235,7 +235,7 @@ describe("typename_IN with auth", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Series.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Series.name}] }] } } } } } @@ -279,7 +279,7 @@ describe("typename_IN with auth", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Series.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Series.name}] }] } } } } } @@ -333,7 +333,7 @@ describe("typename_IN with auth", () => { { where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Movie.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Movie.name}] }] } } } } } @@ -379,7 +379,7 @@ describe("typename_IN with auth", () => { { where: { node: { - actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename_IN: [${Series.name}] }] } } + actedInConnection_SOME: { node: { AND: [ { title_EQ: "The Matrix" }, {typename: [${Series.name}] }] } } } } } diff --git a/packages/graphql/tests/integration/find.int.test.ts b/packages/graphql/tests/integration/find.int.test.ts index 3a2884a29a..7d75befb13 100644 --- a/packages/graphql/tests/integration/find.int.test.ts +++ b/packages/graphql/tests/integration/find.int.test.ts @@ -462,7 +462,7 @@ describe("find", () => { id: ID! title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", direction: OUT) - mainActor: ${Actor}! @relationship(type: "MAIN_ACTOR", direction: OUT) + mainActor: [${Actor}!]! @relationship(type: "MAIN_ACTOR", direction: OUT) } `; diff --git a/packages/graphql/tests/integration/global-node.int.test.ts b/packages/graphql/tests/integration/global-node.int.test.ts index 40dabe9be2..8824bd9b6d 100644 --- a/packages/graphql/tests/integration/global-node.int.test.ts +++ b/packages/graphql/tests/integration/global-node.int.test.ts @@ -35,7 +35,7 @@ describe("Global node resolution", () => { test("returns the correct id after create mutation when the id is autogenerated", async () => { const typeDefs = `type ${typeFilm.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! }`; @@ -145,12 +145,13 @@ describe("Global node resolution", () => { const movie = (gqlResult as { data: { [key: string]: Record[] } }).data[typeFilm.plural]?.[0]; expect(movie).toEqual({ id: expectedId }); }); + test("return the correct id when the underlying field is an aliased id db property", async () => { const typeDefs = gql` type ${typeFilm.name} @node { dbId: ID! @relayId @alias(property: "id") title: String! - createdBy: ${typeUser.name}! @relationship(type: "CREATED_BY", direction: OUT) + createdBy: [${typeUser.name}!]! @relationship(type: "CREATED_BY", direction: OUT) } type ${typeUser.name} @node { @@ -223,12 +224,13 @@ describe("Global node resolution", () => { ], }); }); + test("return the correct id when the underlying field is of type Int", async () => { const typeDefs = gql` type ${typeFilm.name} @node { dbId: Int! @relayId @alias(property: "id") title: String! - createdBy: ${typeUser.name}! @relationship(type: "CREATED_BY", direction: OUT) + createdBy: [${typeUser.name}!]! @relationship(type: "CREATED_BY", direction: OUT) } type ${typeUser.name} @node { @@ -392,17 +394,17 @@ describe("Global node resolution", () => { test("it should throw forbidden when incorrect allow on a top-level node query", async () => { const typeDefs = ` type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! created: [${typeFilm.name}!]! @relationship(type: "CREATED", direction: OUT) } type ${typeFilm.name} @node { title: ID! @relayId - creator: ${typeUser.name}! @relationship(type: "CREATED", direction: IN) + creator: [${typeUser.name}!]! @relationship(type: "CREATED", direction: IN) } - extend type ${typeFilm.name} @authorization(validate: [{ when: [BEFORE], where: { node: { creator: { dbId_EQ: "$jwt.sub" } } } }]) + extend type ${typeFilm.name} @authorization(validate: [{ when: [BEFORE], where: { node: { creator_SINGLE: { dbId_EQ: "$jwt.sub" } } } }]) `; const query = ` @@ -447,7 +449,7 @@ describe("Global node resolution", () => { const typeDefs = ` type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! } @@ -500,7 +502,7 @@ describe("Global node resolution", () => { } type ${typeUser.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! } diff --git a/packages/graphql/tests/integration/interfaces/interface-filtering.int.test.ts b/packages/graphql/tests/integration/interfaces/interface-filtering.int.test.ts index 3191acd5cc..37cba9adc1 100644 --- a/packages/graphql/tests/integration/interfaces/interface-filtering.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/interface-filtering.int.test.ts @@ -31,7 +31,7 @@ describe("Interface filtering", () => { const Actor = testHelper.createUniqueType("Actor"); beforeAll(async () => { - typeDefs = ` + typeDefs = /* GraphQL */ ` interface Show { title: String! actors: [${Actor}!]! @declareRelationship @@ -90,9 +90,9 @@ describe("Interface filtering", () => { }); test("allow for logical filters on top-level interfaces", async () => { - const query = ` + const query = /* GraphQL */ ` query actedInWhere { - shows(where: { OR: [{ title_EQ: "The Office" }, { title_EQ: "The Office 2" }] }) { + shows(where: { OR: [{ title: { eq: "The Office" } }, { title: { eq: "The Office 2" } }] }) { title } } @@ -117,10 +117,10 @@ describe("Interface filtering", () => { }); test("allow for logical filters on nested-level interfaces", async () => { - const query = ` + const query = /* GraphQL */ ` query actedInWhere { ${Actor.plural} { - actedIn(where: { OR: [{ title_EQ: "The Office" }, { title_EQ: "The Office 2" }] }) { + actedIn(where: { OR: [{ title: {eq: "The Office"} }, { title: { eq: "The Office 2"} }] }) { title } } diff --git a/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts b/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts index 1af188e2cc..8e3fa6d5fc 100644 --- a/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/interfaces-top-level.int.test.ts @@ -40,14 +40,14 @@ describe("Top-level interface query fields", () => { typeDefs = ` type ${SomeNodeType} implements MyOtherInterface & MyInterface @node { - id: ID! @id @unique + id: ID! @id something: String somethingElse: String other: [${OtherNodeType}!]! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique - interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) + id: ID! @id + interfaceField: [MyInterface!]! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } interface MyInterface { id: ID! @@ -58,11 +58,11 @@ describe("Top-level interface query fields", () => { } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } type ${MyOtherImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id someField: String } diff --git a/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts b/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts index b3edcd4ca6..e767d31d2a 100644 --- a/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/interfaces.int.test.ts @@ -32,18 +32,18 @@ describe("Interfaces tests", () => { beforeAll(async () => { const typeDefs = ` type ${SomeNodeType} @node { - id: ID! @id @unique - other: ${OtherNodeType}! @relationship(type: "HAS_OTHER_NODES", direction: OUT) + id: ID! @id + other: [${OtherNodeType}!]! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique - interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) + id: ID! @id + interfaceField: [MyInterface!]! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } interface MyInterface { id: ID! } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } extend type ${SomeNodeType} @authentication @@ -91,11 +91,15 @@ describe("Interfaces tests", () => { [SomeNodeType.plural]: [ { id: "1", - other: { - interfaceField: { - id: "3", + other: [ + { + interfaceField: [ + { + id: "3", + }, + ], }, - }, + ], }, ], }); diff --git a/packages/graphql/tests/integration/interfaces/relationships/create/connect.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/create/connect.int.test.ts index 4783474589..19b23fe110 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/create/connect.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/create/connect.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -76,7 +76,7 @@ describe("interface relationships", () => { await testHelper.close(); }); - test("should nested create connect using interface relationship fields", async () => { + test("create->connect->connect should connect using interface relationship fields", async () => { const actorName1 = generate({ readable: true, charset: "alphabetic", @@ -163,4 +163,93 @@ describe("interface relationships", () => { }, }); }); + + test("create->connect->connect should create a new relationship using interface relationship fields", async () => { + const actorName1 = generate({ + readable: true, + charset: "alphabetic", + }); + const actorName2 = generate({ + readable: true, + charset: "alphabetic", + }); + + const movieTitle = generate({ + readable: true, + charset: "alphabetic", + }); + const movieRuntime = 20340; + const movieScreenTime = 87163; + + const query = ` + mutation CreateActorConnectMovie($name1: String!, $title: String, $screenTime: Int!, $name2: String) { + ${Actor.operations.create}( + input: [ + { + name: $name1 + actedIn: { + connect: { + edge: { screenTime: $screenTime } + where: { node: { title_EQ: $title } } + connect: { + actors: { edge: { ActedIn: { screenTime: $screenTime } }, where: { node: { name_EQ: $name2 } } } + } + } + } + } + ] + ) { + ${Actor.plural} { + name + actedIn { + title + actors { + name + } + ... on ${Movie} { + runtime + } + } + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (m:${Movie} { title: $movieTitle, runtime:$movieRuntime }) + CREATE (:${Actor} { name: $name })-[:ACTED_IN { screenTime: $movieScreenTime }]->(m) + + `, + { movieTitle, movieRuntime, name: actorName2, movieScreenTime } + ); + + const gqlResult = await testHelper.executeGraphQL(query, { + variableValues: { + name1: actorName1, + title: movieTitle, + screenTime: movieScreenTime, + name2: actorName2, + }, + }); + + expect(gqlResult.errors).toBeFalsy(); + + expect(gqlResult.data).toEqual({ + [Actor.operations.create]: { + [Actor.plural]: [ + { + actedIn: [ + { + runtime: movieRuntime, + title: movieTitle, + actors: expect.toIncludeSameMembers([{ name: actorName2 }, { name: actorName1 }]), + }, + ], + name: actorName1, + }, + ], + }, + }); + }); }); diff --git a/packages/graphql/tests/integration/interfaces/relationships/create/create.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/create/create.int.test.ts index 19cbed8ba1..38f227a4f5 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/create/create.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/create/create.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-filters.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-filters.int.test.ts index 999cc670c0..823fbbb9b6 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-filters.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-filters.int.test.ts @@ -51,7 +51,7 @@ describe("interface filters of declared relationships", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -248,7 +248,7 @@ describe("interface filters of declared relationships", () => { test("should filter using connection filters + typename_IN + logical", async () => { const query = /* GraphQL */ ` query production { - productions(where: { OR: [{ typename_IN: [${Series}] }, {actorsConnection_SOME: { node: { name_EQ: "${actorName2}" } }}] }) { + productions(where: { OR: [{ typename: [${Series}] }, {actorsConnection_SOME: { node: { name_EQ: "${actorName2}" } }}] }) { title actorsConnection { edges { @@ -349,7 +349,7 @@ describe("interface filters of declared interface relationships", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -718,7 +718,7 @@ describe("interface filters of declared interface relationships", () => { test("should filter using connection filters + typename_IN + logical", async () => { const query = /* GraphQL */ ` query production { - productions(where: { OR: [{ typename_IN: [${Series}] }, {actorsConnection_SOME: { node: { name_EQ: "${actorName2}" } }}] }) { + productions(where: { OR: [{ typename: [${Series}] }, {actorsConnection_SOME: { node: { name_EQ: "${actorName2}" } }}] }) { title actorsConnection { edges { diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-one-level-chain.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-one-level-chain.int.test.ts index 5d2e2892d6..3959328cbe 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-one-level-chain.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-one-level-chain.int.test.ts @@ -38,7 +38,7 @@ describe("interface implementing interface with declared relationships", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Show { diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-simple.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-simple.int.test.ts index 5dc45e98e9..6f4f940248 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-simple.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-simple.int.test.ts @@ -38,7 +38,7 @@ describe("interface with declared relationships", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -853,12 +853,12 @@ describe("interface with declared relationships", () => { node: { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, runtime: movieRuntime }, properties: { screenTime: 0 }, }, - ], + ]), }, }, properties: { screenTime: 0 }, @@ -896,7 +896,7 @@ describe("interface with declared relationships", () => { node: { title: movieTitle, actorsConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { name: actorName, @@ -922,7 +922,7 @@ describe("interface with declared relationships", () => { }, properties: { screenTime: 0 }, }, - ], + ]), }, }, }, @@ -930,7 +930,7 @@ describe("interface with declared relationships", () => { node: { title: seriesTitle, actorsConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { name: actorName, @@ -956,7 +956,7 @@ describe("interface with declared relationships", () => { }, properties: { episodeNr }, }, - ], + ]), }, }, }, @@ -966,7 +966,7 @@ describe("interface with declared relationships", () => { { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, @@ -976,12 +976,12 @@ describe("interface with declared relationships", () => { node: { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, runtime: movieRuntime }, properties: { screenTime: 0 }, }, - ], + ]), }, }, properties: { screenTime: 0 }, @@ -1015,7 +1015,7 @@ describe("interface with declared relationships", () => { }, }, }, - ], + ]), }, }, ] @@ -1146,12 +1146,12 @@ describe("interface with declared relationships", () => { node: { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, runtime: movieRuntime }, properties: { screenTime: movieScreenTime }, }, - ], + ]), }, }, properties: { screenTime: movieScreenTime }, @@ -1189,12 +1189,12 @@ describe("interface with declared relationships", () => { node: { title: movieTitle, actorsConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { name: "custom actor", actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle, @@ -1204,7 +1204,7 @@ describe("interface with declared relationships", () => { screenTime: 101, }, }, - ], + ]), }, }, properties: { screenTime: 101 }, @@ -1234,7 +1234,7 @@ describe("interface with declared relationships", () => { }, properties: { screenTime: movieScreenTime }, }, - ], + ]), }, }, }, @@ -1242,7 +1242,7 @@ describe("interface with declared relationships", () => { node: { title: seriesTitle, actorsConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { name: actorName, @@ -1268,7 +1268,7 @@ describe("interface with declared relationships", () => { }, properties: { episodeNr }, }, - ], + ]), }, }, }, @@ -1278,7 +1278,7 @@ describe("interface with declared relationships", () => { { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, @@ -1288,12 +1288,12 @@ describe("interface with declared relationships", () => { node: { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, runtime: movieRuntime }, properties: { screenTime: movieScreenTime }, }, - ], + ]), }, }, properties: { screenTime: movieScreenTime }, @@ -1327,14 +1327,13 @@ describe("interface with declared relationships", () => { }, }, }, - ], + ]), }, }, ] ); }); - - // update -> connect -> edge + test("update interface relationship, connect edge", async () => { const actorName = "actor1"; const actorName2 = "actor2"; @@ -1352,7 +1351,11 @@ describe("interface with declared relationships", () => { const query = /* GraphQL */ ` mutation { - ${Actor.operations.update}(update: { + ${Actor.operations.update}( + where: { + name: { eq: "${actorName}" } + } + update: { actedIn: [{ # ActorActedInUpdateFieldInput where: { node: { title_EQ: "${movieTitle}" } } # ActorActedInConnectionWhere update: { # ActorActedInUpdateConnectionInput @@ -1449,69 +1452,21 @@ describe("interface with declared relationships", () => { name: actorName, actedInConnection: { edges: expect.toIncludeSameMembers([ - { - node: { - title: movieTitle2, - actorsConnection: { - edges: expect.toIncludeSameMembers([ - { - node: { - name: actorName2, - actedInConnection: { - edges: [ - { - node: { title: movieTitle2, runtime: movieRuntime }, - properties: { screenTime: movieScreenTime }, - }, - ], - }, - }, - properties: { screenTime: movieScreenTime }, - }, - { - node: { - name: actorName, - actedInConnection: { - edges: expect.toIncludeSameMembers([ - { - node: { title: movieTitle2, runtime: movieRuntime }, - properties: { screenTime: movieScreenTime }, - }, - { - node: { title: movieTitle, runtime: movieRuntime }, - properties: { screenTime: movieScreenTime }, - }, - { - node: { - title: seriesTitle, - episodeCount: seriesEpisodes, - }, - properties: { screenTime: movieScreenTime }, - }, - ]), - }, - }, - properties: { screenTime: movieScreenTime }, - }, - ]), - }, - }, - }, { node: { title: movieTitle, actorsConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { name: actorName3, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle, runtime: movieRuntime }, properties: { screenTime: 111 }, }, - ], + ]), }, }, properties: { screenTime: 111 }, @@ -1541,51 +1496,10 @@ describe("interface with declared relationships", () => { }, properties: { screenTime: movieScreenTime }, }, - ], - }, - }, - }, - { - node: { - title: seriesTitle, - actorsConnection: { - edges: [ - { - node: { - name: actorName, - actedInConnection: { - edges: expect.toIncludeSameMembers([ - { - node: { title: movieTitle2, runtime: movieRuntime }, - properties: { screenTime: movieScreenTime }, - }, - { - node: { title: movieTitle, runtime: movieRuntime }, - properties: { screenTime: movieScreenTime }, - }, - { - node: { - title: seriesTitle, - episodeCount: seriesEpisodes, - }, - properties: { screenTime: movieScreenTime }, - }, - ]), - }, - }, - properties: { episodeNr }, - }, - ], + ]), }, }, }, - ]), - }, - }, - { - name: actorName2, - actedInConnection: { - edges: [ { node: { title: movieTitle2, @@ -1595,12 +1509,12 @@ describe("interface with declared relationships", () => { node: { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle2, runtime: movieRuntime }, properties: { screenTime: movieScreenTime }, }, - ], + ]), }, }, properties: { screenTime: movieScreenTime }, @@ -1634,32 +1548,11 @@ describe("interface with declared relationships", () => { }, }, }, - ], - }, - }, - { - name: actorName3, - actedInConnection: { - edges: expect.toIncludeSameMembers([ { node: { - title: movieTitle, + title: seriesTitle, actorsConnection: { - edges: [ - { - node: { - name: actorName3, - actedInConnection: { - edges: [ - { - node: { title: movieTitle, runtime: movieRuntime }, - properties: { screenTime: 111 }, - }, - ], - }, - }, - properties: { screenTime: 111 }, - }, + edges: expect.toIncludeSameMembers([ { node: { name: actorName, @@ -1683,9 +1576,9 @@ describe("interface with declared relationships", () => { ]), }, }, - properties: { screenTime: movieScreenTime }, + properties: { episodeNr }, }, - ], + ]), }, }, }, @@ -1712,9 +1605,8 @@ describe("interface with declared relationships", () => { edge: { screenTime: 112 } where: { node: { title_EQ: "${movieTitle}" } } connect: { - actors: [{ + actors: [{ edge: { ActedIn: { screenTime: 111 }, StarredIn: { episodeNr: 111 } }, - }] } }] @@ -1786,7 +1678,7 @@ describe("interface with declared relationships", () => { { name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle, @@ -1794,37 +1686,59 @@ describe("interface with declared relationships", () => { edges: expect.toIncludeSameMembers([ { node: { - name: actorName2, + name: actorName, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle, runtime: movieRuntime }, properties: { screenTime: 111 }, }, - ], + ]), }, }, properties: { screenTime: 111 }, }, { node: { - name: actorName, + name: actorName2, actedInConnection: { - edges: [ + edges: expect.toIncludeSameMembers([ { node: { title: movieTitle, runtime: movieRuntime }, properties: { screenTime: 111 }, }, - ], + { + node: { title: movieTitle, runtime: movieRuntime }, + properties: { screenTime: 112 }, + }, + ]), }, }, properties: { screenTime: 111 }, }, + { + node: { + name: actorName2, + actedInConnection: { + edges: expect.toIncludeSameMembers([ + { + node: { title: movieTitle, runtime: movieRuntime }, + properties: { screenTime: 111 }, + }, + { + node: { title: movieTitle, runtime: movieRuntime }, + properties: { screenTime: 112 }, + }, + ]), + }, + }, + properties: { screenTime: 112 }, + }, ]), }, }, }, - ], + ]), }, }, ] @@ -1853,7 +1767,7 @@ describe("interface with declared relationships", () => { actedIn: { connect: { edge: { screenTime: 10 } - where: { node: { title_EQ: "${movieTitle}", typename_IN: [${Movie.name}] } } + where: { node: { title_EQ: "${movieTitle}", typename: [${Movie.name}] } } } } } diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-three-level-chain.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-three-level-chain.int.test.ts index 6a73a5f478..6ad9b7bda0 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-three-level-chain.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-three-level-chain.int.test.ts @@ -38,7 +38,7 @@ describe("interface implementing interface with declared relationships - three l const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Thing { diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-two-level-chain.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-two-level-chain.int.test.ts index fa1e77e793..62ae6f1cdf 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-two-level-chain.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/interface-two-level-chain.int.test.ts @@ -38,7 +38,7 @@ describe("interface implementing interface with declared relationships - two lev const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Thing { diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing-nested.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing-nested.int.test.ts index dd7af989e5..74216d65f2 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing-nested.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing-nested.int.test.ts @@ -438,7 +438,7 @@ describe("type narrowing nested connections", () => { query UntrainedPeople { ${UntrainedPerson.plural} { name - actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: 0 } } }) { + actedInConnection(where: { edge: { AppearsIn: { sceneNr: { eq: 0 } } } }) { edges { node { title @@ -528,7 +528,7 @@ describe("type narrowing nested connections", () => { query UntrainedPeople { ${UntrainedPerson.plural} { name - actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: ${sceneNr} }, ActedIn: {screenTime_EQ: ${movieScreenTime}} } }) { + actedInConnection(where: { edge: { AppearsIn: { sceneNr:{ eq: ${sceneNr}} }, ActedIn: {screenTime: { eq: ${movieScreenTime} }} } }) { edges { node { title diff --git a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing.int.test.ts index 160dc28d23..29d8df5a8e 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/declare-relationship/type-narrowing.int.test.ts @@ -569,7 +569,7 @@ describe("type narrowing - simple case", () => { const query = /* GraphQL */ ` query People { - people(where: { actedInConnection_SOME: { edge: { ActedIn: { screenTime_EQ: ${movieScreenTime} }, AppearsIn: { sceneNr_EQ: ${sceneNr} } } } }) { + people(where: { actedInConnection_SOME: { edge: { ActedIn: { screenTime: {eq: ${movieScreenTime} } }, AppearsIn: { sceneNr: { eq: ${sceneNr} } } } } }) { name actedInConnection { edges { @@ -688,7 +688,7 @@ describe("type narrowing - simple case", () => { const query = /* GraphQL */ ` query People { - people(where: { actedInConnection_SOME: { node: { OR: [ { title_EQ: "${movieTitle}" }, { title_EQ: "${amatureProductionTitle}" }] } } }) { + people(where: { actedInConnection_SOME: { node: { OR: [ { title: { eq: "${movieTitle}" } }, { title: { eq: "${amatureProductionTitle}" }}] } } }) { name actedInConnection { edges { @@ -811,7 +811,7 @@ describe("type narrowing - simple case", () => { edges { node { title - actorsConnection(where: { edge: { ActedIn: {screenTime_EQ: ${movieScreenTime2}}, AppearsIn: {} } }) { + actorsConnection(where: { edge: { ActedIn: {screenTime: { eq: ${movieScreenTime2}} }, AppearsIn: {} } }) { edges { node { name @@ -971,7 +971,7 @@ describe("type narrowing - simple case", () => { edges { node { title - actorsConnection(where: { edge: { AppearsIn: { NOT: { sceneNr_EQ: ${sceneNr} } } } }) { + actorsConnection(where: { edge: { AppearsIn: { NOT: { sceneNr: { eq: ${sceneNr}} } } } }) { edges { node { name @@ -1131,7 +1131,7 @@ describe("type narrowing - simple case", () => { edges { node { title - actorsConnection(where: { edge: { ActedIn: { NOT: { screenTime_EQ: ${movieScreenTime} } }, AppearsIn: { NOT: { sceneNr_EQ: ${sceneNr} } } } }) { + actorsConnection(where: { edge: { ActedIn: { NOT: { screenTime: { eq: ${movieScreenTime}} } }, AppearsIn: { NOT: { sceneNr: { eq: ${sceneNr} } } } } }) { edges { node { name @@ -1277,7 +1277,7 @@ describe("type narrowing - simple case", () => { query Actors { ${Actor.plural} { name - actedInConnection(where: { edge: { ActedIn: { screenTime_EQ: ${movieScreenTime} } } }) { + actedInConnection(where: { edge: { ActedIn: { screenTime: { eq: ${movieScreenTime}} } } }) { edges { node { title @@ -1371,7 +1371,7 @@ describe("type narrowing - simple case", () => { query Actors { ${Actor.plural} { name - actedInConnection(where: { edge: { AppearsIn: { sceneNr_EQ: 0 } } }) { + actedInConnection(where: { edge: { AppearsIn: { sceneNr:{ eq: 0 }} } }) { edges { node { title diff --git a/packages/graphql/tests/integration/interfaces/relationships/delete/delete.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/delete/delete.int.test.ts index 9731aab441..6befc08595 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/delete/delete.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/delete/delete.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/read.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/read.int.test.ts index d5f0dc5ff1..cadcd85806 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/read.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/read.int.test.ts @@ -54,7 +54,7 @@ describe("interface relationships", () => { type ${typeActor} @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) + currentlyActingIn: [Production!]! @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; @@ -285,10 +285,12 @@ describe("interface relationships", () => { expect(gqlResult.data).toEqual({ [typeActor.plural]: [ { - currentlyActingIn: { - title: newMovieTitle, - runtime: newMovieRuntime, - }, + currentlyActingIn: [ + { + title: newMovieTitle, + runtime: newMovieRuntime, + }, + ], name: actorName, }, ], diff --git a/packages/graphql/tests/integration/interfaces/relationships/update/connect.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/update/connect.int.test.ts index 809eb59d37..f15f05e26b 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/update/connect.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/update/connect.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/update/create.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/update/create.int.test.ts index 5cc556e792..9b515f680e 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/update/create.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/update/create.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/update/delete.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/update/delete.int.test.ts index b94f2de451..a5c9e41e79 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/update/delete.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/update/delete.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/update/disconnect.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/update/disconnect.int.test.ts index 22dca19121..94c13ac7b7 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/update/disconnect.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/update/disconnect.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/interfaces/relationships/update/update.int.test.ts b/packages/graphql/tests/integration/interfaces/relationships/update/update.int.test.ts index f7454fac14..81de4d130f 100644 --- a/packages/graphql/tests/integration/interfaces/relationships/update/update.int.test.ts +++ b/packages/graphql/tests/integration/interfaces/relationships/update/update.int.test.ts @@ -37,7 +37,7 @@ describe("interface relationships", () => { const typeDefs = /* GraphQL */ ` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/issues/1115.int.test.ts b/packages/graphql/tests/integration/issues/1115.int.test.ts deleted file mode 100644 index cbf322067d..0000000000 --- a/packages/graphql/tests/integration/issues/1115.int.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1115", () => { - let parentType: UniqueType; - let childType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - parentType = testHelper.createUniqueType("Parent"); - childType = testHelper.createUniqueType("Child"); - - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${parentType} @node { - children: [${childType}!]! @relationship(type: "HAS", direction: IN) - } - - type ${childType} @node { - tcId: String @unique - } - - extend type ${childType} - @authorization( - validate: [ - { operations: [READ, CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP], where: { jwt: { roles_INCLUDES: "upstream" } } } - { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } - ] - ) - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: "secret", - }, - }, - }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not throw on multiple connectOrCreate with auth", async () => { - await testHelper.executeCypher(`CREATE (:${parentType})<-[:HAS]-(:${childType} {tcId: "123"})`); - - const token = testHelper.createBearerToken("secret", { roles: ["upstream"] }); - const query = /* GraphQL */ ` - mutation { - ${parentType.operations.update}( - update: { - children: [ - { - connectOrCreate: { - where: { node: { tcId_EQ: "123" } } - onCreate: { node: { tcId: "123" } } - }} - { - connectOrCreate: { - where: { node: { tcId_EQ: "456" } } - onCreate: { node: { tcId: "456" } } - }} - ] - } - ) { - info { - nodesCreated - } - } - } - `; - - const res = await testHelper.executeGraphQLWithToken(query, token); - - expect(res.errors).toBeUndefined(); - expect(res.data).toEqual({ - [parentType.operations.update]: { info: { nodesCreated: 1 } }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1121.int.test.ts b/packages/graphql/tests/integration/issues/1121.int.test.ts index dbe808f586..dc56962978 100644 --- a/packages/graphql/tests/integration/issues/1121.int.test.ts +++ b/packages/graphql/tests/integration/issues/1121.int.test.ts @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/1121", () => { } type ${Banana.name} implements Ingredient_Interface @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float @@ -58,14 +58,14 @@ describe("https://github.com/neo4j/graphql/issues/1121", () => { union Ingredient_Union = ${Sugar.name} | ${Syrup.name} type ${Sugar.name} @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float } type ${Syrup.name} @node { - id: ID! @id @unique + id: ID! @id name: String sweetness: String qty_ozs: Float diff --git a/packages/graphql/tests/integration/issues/1127.int.test.ts b/packages/graphql/tests/integration/issues/1127.int.test.ts deleted file mode 100644 index 9ebd076249..0000000000 --- a/packages/graphql/tests/integration/issues/1127.int.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1127", () => { - let customerType: UniqueType; - let addressType: UniqueType; - let postalCodeType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - customerType = testHelper.createUniqueType("Customer"); - addressType = testHelper.createUniqueType("Address"); - postalCodeType = testHelper.createUniqueType("PostalCode"); - - const typeDefs = ` - type ${customerType.name} @node { - uuid: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - - address: ${addressType.name}! @relationship(type: "TEST_HAS_ADDRESS", direction: OUT) - } - - type ${addressType.name} @node { - uuid: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - - postalCode: ${postalCodeType.name}! @relationship(type: "TEST_HAS_POSTAL_CODE", direction: OUT) - } - - type ${postalCodeType.name} @node { - number: String! @unique - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should be able to connectOrCreate under nested create", async () => { - const query = ` - mutation CreateTestCustomers($input: [${customerType.name}CreateInput!]!) { - ${customerType.operations.create}(input: $input) { - info { - nodesCreated - relationshipsCreated - } - ${customerType.plural} { - address { - postalCode { - number - } - } - } - } - } - `; - - const res = await testHelper.executeGraphQL(query, { - variableValues: { - input: [ - { - address: { - create: { - node: { - postalCode: { - connectOrCreate: { - where: { - node: { - number_EQ: "00001", - }, - }, - onCreate: { - node: { - number: "00001", - }, - }, - }, - }, - }, - }, - }, - }, - ], - }, - }); - - expect(res.errors).toBeUndefined(); - - expect(res.data).toEqual({ - [customerType.operations.create]: { - info: { - nodesCreated: 3, - relationshipsCreated: 2, - }, - [customerType.plural]: [ - { - address: { - postalCode: { - number: "00001", - }, - }, - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1150.int.test.ts b/packages/graphql/tests/integration/issues/1150.int.test.ts index 8fbad714d3..bf93182c38 100644 --- a/packages/graphql/tests/integration/issues/1150.int.test.ts +++ b/packages/graphql/tests/integration/issues/1150.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { } type ${Battery} @node { - id: ID! @unique + id: ID! current: Boolean! } @@ -51,12 +51,12 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "admin" } } }]) type ${CombustionEngine} @node { - id: ID! @unique + id: ID! current: Boolean! } type ${Drive} @node { - id: ID! @unique + id: ID! current: Boolean! driveCompositions: [${DriveComposition}!]! @relationship(type: "CONSISTS_OF", properties: "RelationProps", direction: OUT) @@ -65,7 +65,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { union DriveComponent = ${Battery} | ${CombustionEngine} type ${DriveComposition} @node { - id: ID! @unique + id: ID! current: Boolean! driveComponent: [DriveComponent!]! @relationship(type: "HAS", properties: "RelationProps", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/1221.int.test.ts b/packages/graphql/tests/integration/issues/1221.int.test.ts index e50e11c8aa..f92a247782 100644 --- a/packages/graphql/tests/integration/issues/1221.int.test.ts +++ b/packages/graphql/tests/integration/issues/1221.int.test.ts @@ -38,9 +38,9 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { testNameDetails = testHelper.createUniqueType("NameDetails"); testMasterData = testHelper.createUniqueType("MasterData"); - typeDefs = ` + typeDefs = /* GraphQL */ ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -55,15 +55,15 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! - nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [${testNameDetails}!]! @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } `; extendedTypeDefs = ` type ${testMain} @node { - id: ID! @unique + id: ID! current: Boolean! main: [${testSeries}!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) } @@ -91,7 +91,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { CREATE (:${testNameDetails} { fullName: "MHBB" })<-[:HAS_NAME { current: true }]-(:${testMasterData} { current: true, id: "523" })<-[:ARCHITECTURE { current: true }]-(:${testSeries} { current: true, id: "621" }) `); - const query = ` + const query = /* GraphQL */ ` query ( $where: ${testSeries}Where = { current_EQ: true } $connectionWhere: RelationPropsWhere = { current_EQ: true } @@ -120,7 +120,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { current_EQ: true, architectureConnection_SINGLE: { node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { node: { fullName_EQ: "MHA", }, @@ -207,7 +207,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { current_EQ: true, architectureConnection_SINGLE: { node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { node: { fullName_EQ: "MHA", }, @@ -315,7 +315,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { current_EQ: true, architectureConnection_SINGLE: { node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { node: { fullName_EQ: "MHA", }, @@ -397,8 +397,8 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { const query = ` query ( - $where: ${testMain}Where = { current: true } - $connectionWhere: RelationPropsWhere = { current: true } + $where: ${testMain}Where = { current_EQ: true } + $connectionWhere: RelationPropsWhere = { current_EQ: true } ) { ${testMain.plural}(where: $where) { id @@ -432,7 +432,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { node: { architectureConnection_SINGLE: { node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { node: { fullName_EQ: "MHA", }, @@ -540,7 +540,7 @@ describe("https://github.com/neo4j/graphql/issues/1221", () => { node: { architectureConnection_SINGLE: { node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { node: { fullName_EQ: "MHA", }, diff --git a/packages/graphql/tests/integration/issues/1249.int.test.ts b/packages/graphql/tests/integration/issues/1249.int.test.ts index 103ebc608c..8f2d00b761 100644 --- a/packages/graphql/tests/integration/issues/1249.int.test.ts +++ b/packages/graphql/tests/integration/issues/1249.int.test.ts @@ -38,7 +38,7 @@ describe("https://github.com/neo4j/graphql/issues/1249", () => { @node(labels: ["Bulk", "$tenant"]) { id: ID! supplierMaterialNumber: String! - material: ${Material}! @relationship(type: "MATERIAL_BULK", direction: OUT) + material: [${Material}!]! @relationship(type: "MATERIAL_BULK", direction: OUT) } type ${Material} @mutation(operations: []) @node { diff --git a/packages/graphql/tests/integration/issues/1287.int.test.ts b/packages/graphql/tests/integration/issues/1287.int.test.ts index 55a986c4b5..1af6e7f256 100644 --- a/packages/graphql/tests/integration/issues/1287.int.test.ts +++ b/packages/graphql/tests/integration/issues/1287.int.test.ts @@ -34,10 +34,10 @@ describe("https://github.com/neo4j/graphql/issues/1287", () => { typeDefs = ` type ${screeningsType} @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! - movie: ${norwegianScreenable}! @relationship(type: "SCREENS_MOVIE", direction: OUT) + movie: [${norwegianScreenable}!]! @relationship(type: "SCREENS_MOVIE", direction: OUT) } interface ScreenableMeta { @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/1287", () => { } type ${norwegianScreenable} implements ScreenableMeta @node { - id: ID! @id @unique + id: ID! @id spokenLanguage: String! subtitlesLanguage: String! premiere: DateTime! @@ -70,7 +70,7 @@ describe("https://github.com/neo4j/graphql/issues/1287", () => { const query = ` query queryScreenings { - ${screeningsType.plural}(where: { movieConnection: { node: { id_EQ: "my-id" } } }) { + ${screeningsType.plural}(where: { movieConnection_SINGLE: { node: { id_EQ: "my-id" } } }) { beginsAt movie { id diff --git a/packages/graphql/tests/integration/issues/1320.int.test.ts b/packages/graphql/tests/integration/issues/1320.int.test.ts index e04548b5b6..c107416d60 100644 --- a/packages/graphql/tests/integration/issues/1320.int.test.ts +++ b/packages/graphql/tests/integration/issues/1320.int.test.ts @@ -36,8 +36,8 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { const typeDefs = gql` type ${riskType.name} @node { code: String! - ownedBy: ${teamType.name} @relationship(type: "OWNS_RISK", direction: IN) - mitigationState: [${mitigationStateType.name}] + ownedBy: [${teamType.name}!]! @relationship(type: "OWNS_RISK", direction: IN) + mitigationState: [${mitigationStateType.name}!] } type ${teamType.name} @node { diff --git a/packages/graphql/tests/integration/issues/1348.int.test.ts b/packages/graphql/tests/integration/issues/1348.int.test.ts index e027f41ab1..49206e71e0 100644 --- a/packages/graphql/tests/integration/issues/1348.int.test.ts +++ b/packages/graphql/tests/integration/issues/1348.int.test.ts @@ -40,14 +40,14 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { type ${Series} implements Product @node { productTitle: String! - relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) seasons: [${Season}!]! } type ${Season} implements Product @node { productTitle: String! - relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) seasonNumber: Int episodes: [${ProgrammeItem}!]! @@ -55,7 +55,7 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { type ${ProgrammeItem} implements Product @node { productTitle: String! - relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + relatedTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) episodeNumber: Int } diff --git a/packages/graphql/tests/integration/issues/1430.int.test.ts b/packages/graphql/tests/integration/issues/1430.int.test.ts deleted file mode 100644 index ab11024bab..0000000000 --- a/packages/graphql/tests/integration/issues/1430.int.test.ts +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1430", () => { - let testAbce: UniqueType; - let testChildOne: UniqueType; - let testChildTwo: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - testAbce = testHelper.createUniqueType("ABCE"); - testChildOne = testHelper.createUniqueType("ChildOne"); - testChildTwo = testHelper.createUniqueType("ChildTwo"); - - const typeDefs = ` - type ${testAbce.name} @node { - id:ID @id @unique - name: String - interface: InterfaceMom @relationship(type:"HAS_INTERFACE", direction:OUT) - } - - interface InterfaceMom { - id:ID - name:String - } - - type ${testChildOne.name} implements InterfaceMom @node { - id:ID @id @unique - name:String - feathur: String - } - - type ${testChildTwo.name} implements InterfaceMom @node { - id:ID @id @unique - name:String - sth: String - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not allow to create more than one node for a one-to-one relationship", async () => { - const createMutation = ` - mutation createAbces { - ${testAbce.operations.create}( - input: [ - { - interface: { - create: { - node: { - ${testChildOne.name}: { name: "childone name" }, - ${testChildTwo.name}: { name: "childtwo name" } - } - } - } - } - ] - ) { - ${testAbce.plural} { - id - name - interface { - id - name - __typename - } - } - } - } - `; - - const createMutationResults = await testHelper.executeGraphQL(createMutation); - - expect(createMutationResults.errors).toHaveLength(1); - expect(createMutationResults.errors?.[0]?.message).toBe( - `Relationship field "${testAbce.name}.interface" cannot have more than one node linked` - ); - expect(createMutationResults.data as any).toBeNull(); - }); - - test("should not allow creating a second node to an existing one-to-one relationship", async () => { - const createMutation = ` - mutation createAbces { - ${testAbce.operations.create}( - input: [ - { - interface: { - create: { - node: { - ${testChildOne.name}: { name: "childone name second round" }, - } - } - } - } - ] - ) { - ${testAbce.plural} { - id - name - interface { - id - name - __typename - } - } - } - } - `; - - const createMutationResults = await testHelper.executeGraphQL(createMutation); - - expect(createMutationResults.errors).toBeUndefined(); - expect(createMutationResults.data as any).toEqual({ - [testAbce.operations.create]: { - [testAbce.plural]: [ - { - id: expect.any(String), - name: null, - interface: { - id: expect.any(String), - name: "childone name second round", - __typename: testChildOne.name, - }, - }, - ], - }, - }); - - const abcesId = (createMutationResults.data as any)[testAbce.operations.create][testAbce.plural][0].id; - - const updateMutation = ` - mutation ddfs{ - ${testAbce.operations.update}(where: { id: "${abcesId}" } - update: { interface: { create: { node: { ${testChildOne.name}: { name: "childone name2" } } } } } - ){ - ${testAbce.plural} { - id - interface { - id - name - __typename - } - } - } - } - `; - - const updateMutationResults = await testHelper.executeGraphQL(updateMutation); - - expect(updateMutationResults.errors).toHaveLength(1); - expect(updateMutationResults.errors?.[0]?.message).toContain( - `Relationship field "${testAbce.name}.interface" cannot have more than one node linked` - ); - expect(updateMutationResults.data as any).toBeNull(); - }); - - test("should not allow connecting a second node to an existing one-to-one relationship", async () => { - const createMutation = ` - mutation createAbces { - ${testAbce.operations.create}( - input: [ - { - interface: { - create: { - node: { - ${testChildOne.name}: { name: "childone name connect" }, - } - } - } - } - ] - ) { - ${testAbce.plural} { - id - name - interface { - id - name - __typename - } - } - } - } - `; - - const createMutationResults = await testHelper.executeGraphQL(createMutation); - - expect(createMutationResults.errors).toBeUndefined(); - expect(createMutationResults.data as any).toEqual({ - [testAbce.operations.create]: { - [testAbce.plural]: [ - { - id: expect.any(String), - name: null, - interface: { - id: expect.any(String), - name: "childone name connect", - __typename: testChildOne.name, - }, - }, - ], - }, - }); - - const abcesId = (createMutationResults.data as any)[testAbce.operations.create][testAbce.plural][0].id; - - const updateMutation = ` - mutation { - ${testAbce.operations.update}( - where: { id: "${abcesId}" } - update: { interface: { connect: { where: { node: { name: "childone name connect" } } } } } - ) { - ${testAbce.plural} { - id - interface { - id - name - __typename - } - } - } - } - `; - - const updateMutationResults = await testHelper.executeGraphQL(updateMutation); - - expect(updateMutationResults.errors).toHaveLength(1); - expect(updateMutationResults.errors?.[0]?.message).toContain( - `Relationship field "${testAbce.name}.interface" cannot have more than one node linked` - ); - expect(updateMutationResults.data as any).toBeNull(); - }); - - test("should not allow a nested create of a second node to an existing one-to-one relationship", async () => { - const createMutation = ` - mutation createAbces { - ${testAbce.operations.create}( - input: [ - { - interface: { - create: { - node: { - ${testChildOne.name}: { name: "childone name nested create" }, - } - } - } - } - ] - ) { - ${testAbce.plural} { - id - name - interface { - id - name - __typename - } - } - } - } - `; - - const createMutationResults = await testHelper.executeGraphQL(createMutation); - - expect(createMutationResults.errors).toBeUndefined(); - expect(createMutationResults.data as any).toEqual({ - [testAbce.operations.create]: { - [testAbce.plural]: [ - { - id: expect.any(String), - name: null, - interface: { - id: expect.any(String), - name: "childone name nested create", - __typename: testChildOne.name, - }, - }, - ], - }, - }); - - const abcesId = (createMutationResults.data as any)[testAbce.operations.create][testAbce.plural][0].id; - - const updateMutation = ` - mutation { - ${testAbce.operations.update}( - where: { id: "${abcesId}" } - update: { interface: { create: { node: { ${testChildOne.name}: { name: "childone anme nested create" } } } } } - ) { - ${testAbce.plural} { - id - interface { - id - name - __typename - } - } - } - } - `; - - const updateMutationResults = await testHelper.executeGraphQL(updateMutation); - - expect(updateMutationResults.errors).toHaveLength(1); - expect(updateMutationResults.errors?.[0]?.message).toContain( - `Relationship field "${testAbce.name}.interface" cannot have more than one node linked` - ); - expect(updateMutationResults.data as any).toBeNull(); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1535.int.test.ts b/packages/graphql/tests/integration/issues/1535.int.test.ts index e0c203a9a5..ff1a626d6c 100644 --- a/packages/graphql/tests/integration/issues/1535.int.test.ts +++ b/packages/graphql/tests/integration/issues/1535.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { const typeDefs = ` type ${testTenant} @node { - id: ID! @id @unique + id: ID! @id name: String! events: [Event!]! @relationship(type: "HOSTED_BY", direction: IN) fooBars: [${FooBar}!]! @relationship(type: "HAS_FOOBARS", direction: OUT) @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type Screening implements Event @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! } @@ -60,7 +60,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type ${FooBar} @node { - id: ID! @id @unique + id: ID! @id name: String! } `; diff --git a/packages/graphql/tests/integration/issues/1536.int.test.ts b/packages/graphql/tests/integration/issues/1536.int.test.ts index 44ecd00237..13aa5830fb 100644 --- a/packages/graphql/tests/integration/issues/1536.int.test.ts +++ b/packages/graphql/tests/integration/issues/1536.int.test.ts @@ -34,13 +34,13 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { const typeDefs = ` type ${SomeNodeType} @node { - id: ID! @id @unique - other: ${OtherNodeType}! @relationship(type: "HAS_OTHER_NODES", direction: OUT) + id: ID! @id + other: [${OtherNodeType}!]! @relationship(type: "HAS_OTHER_NODES", direction: OUT) } type ${OtherNodeType} @node { - id: ID! @id @unique - interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) + id: ID! @id + interfaceField: [MyInterface!]! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) } interface MyInterface { @@ -48,7 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { } type ${MyImplementationType} implements MyInterface @node { - id: ID! @id @unique + id: ID! @id } `; @@ -84,11 +84,15 @@ describe("https://github.com/neo4j/graphql/issues/1536", () => { [SomeNodeType.plural]: [ { id: "1", - other: { - interfaceField: { - id: "3", + other: [ + { + interfaceField: [ + { + id: "3", + }, + ], }, - }, + ], }, ], }); diff --git a/packages/graphql/tests/integration/issues/1575.int.test.ts b/packages/graphql/tests/integration/issues/1575.int.test.ts index b1f5b79127..34e5bd95fe 100644 --- a/packages/graphql/tests/integration/issues/1575.int.test.ts +++ b/packages/graphql/tests/integration/issues/1575.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { const query = /* GraphQL */ ` mutation MyMutation { updateFoos( - update: { geo_point: { longitude: 1, latitude: 1.5 }, point: { longitude: 2, latitude: 1.5 } } + update: { geo_point_SET: { longitude: 1, latitude: 1.5 }, point_SET: { longitude: 2, latitude: 1.5 } } ) { foos { point { @@ -57,7 +57,7 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { const result = await testHelper.executeGraphQL(query); expect(result.errors).toEqual([ - new GraphQLError("Conflicting modification of [[point]], [[geo_point]] on type Foo"), + new GraphQLError("Conflicting modification of [[point_SET]], [[geo_point_SET]] on type Foo"), ]); }); }); diff --git a/packages/graphql/tests/integration/issues/1628.int.test.ts b/packages/graphql/tests/integration/issues/1628.int.test.ts index 9cef9a5e4f..20a69093b1 100644 --- a/packages/graphql/tests/integration/issues/1628.int.test.ts +++ b/packages/graphql/tests/integration/issues/1628.int.test.ts @@ -30,7 +30,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { """ IRI """ - iri: ID! @unique @alias(property: "uri") + iri: ID! @alias(property: "uri") title: [${titleType}!]! @relationship(type: "title", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/1735.int.test.ts b/packages/graphql/tests/integration/issues/1735.int.test.ts index 9e1b3142b9..7a401c4521 100644 --- a/packages/graphql/tests/integration/issues/1735.int.test.ts +++ b/packages/graphql/tests/integration/issues/1735.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${movieType.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! actors: [${actorType.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "MovieActorEdgeProperties") leadActorsCount: Int! @cypher(statement:""" @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${actorType.name} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [${movieType.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "MovieActorEdgeProperties") } diff --git a/packages/graphql/tests/integration/issues/1751.int.test.ts b/packages/graphql/tests/integration/issues/1751.int.test.ts index 63c827c736..551525fe6f 100644 --- a/packages/graphql/tests/integration/issues/1751.int.test.ts +++ b/packages/graphql/tests/integration/issues/1751.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/1735", () => { } type ${adminType} @node { - adminId: ID! @id @unique + adminId: ID! @id organizations: [${organizationType}!]! @relationship(type: "HAS_ADMINISTRATOR", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/1756.int.test.ts b/packages/graphql/tests/integration/issues/1756.int.test.ts deleted file mode 100644 index f691b2e217..0000000000 --- a/packages/graphql/tests/integration/issues/1756.int.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/1756", () => { - let productType: UniqueType; - let genreType: UniqueType; - - const testHelper = new TestHelper(); - - beforeAll(async () => { - productType = testHelper.createUniqueType("Product"); - genreType = testHelper.createUniqueType("Genre"); - const typeDefs = ` - interface INode { - id: ID! - } - - type ${productType.name} implements INode @node { - id: ID! @populatedBy(operations: [CREATE], callback: "nanoid") - name: String! - genre: [${genreType.name}!]! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type ${genreType.name} implements INode @node { - id: ID! @populatedBy(operations: [CREATE], callback: "nanoid") - value: String! @unique - } - `; - - const nanoid = () => { - return `callback_value`; - }; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { populatedBy: { callbacks: { nanoid } } } }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not raise a GraphQL validation error if invoked without passing the id field", async () => { - const query = ` - mutation { - ${productType.operations.create}(input: { - name: "TestProduct", - genre: { - connectOrCreate: [ - { - where: { - node: { - value_EQ: "Action" - } - }, - onCreate: { - node: { - value: "Action" - } - } - } - ] - } - }) { - ${productType.plural} { - id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeFalsy(); - expect(result?.data?.[productType.operations.create]).toEqual({ - [productType.plural]: [ - { - id: "callback_value", - }, - ], - }); - }); - test("should define the ID using the callback function", async () => { - const query = ` - mutation { - ${productType.operations.create}(input: { - name: "TestProduct", - genre: { - connectOrCreate: [ - { - where: { - node: { - value_EQ: "Action" - } - }, - onCreate: { - node: { - value: "Action" - } - } - } - ] - } - }) { - ${productType.plural} { - id - name - genre { - id - value - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeFalsy(); - expect(result?.data?.[productType.operations.create]).toEqual({ - [productType.plural]: [ - { - id: "callback_value", - name: "TestProduct", - genre: [ - { - id: "callback_value", - value: "Action", - }, - ], - }, - ], - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/1760.int.test.ts b/packages/graphql/tests/integration/issues/1760.int.test.ts index e848e02b8c..57cc3818a1 100644 --- a/packages/graphql/tests/integration/issues/1760.int.test.ts +++ b/packages/graphql/tests/integration/issues/1760.int.test.ts @@ -44,19 +44,19 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { interface BusinessObject { id: ID! - nameDetails: ${NameDetails} + nameDetails: [${NameDetails}!]! } type ${ApplicationVariant} implements BusinessObject @node @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { markets: [${Market}!]! @relationship(type: "HAS_MARKETS", direction: OUT) - id: ID! @unique + id: ID! relatedId: ID @cypher(statement: "MATCH (this)<-[:HAS_BASE]-(n:${BaseObject}) RETURN n.id as res", columnName: "res") - baseObject: ${BaseObject}! @relationship(type: "HAS_BASE", direction: IN) + baseObject: [${BaseObject}!]! @relationship(type: "HAS_BASE", direction: IN) current: Boolean! - nameDetails: ${NameDetails} @relationship(type: "HAS_NAME", direction: OUT) + nameDetails: [${NameDetails}!]! @relationship(type: "HAS_NAME", direction: OUT) } type ${NameDetails} @node @@ -69,14 +69,14 @@ describe("https://github.com/neo4j/graphql/issues/1760", () => { type ${Market} implements BusinessObject @node @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @unique - nameDetails: ${NameDetails} @relationship(type: "HAS_NAME", direction: OUT) + id: ID! + nameDetails: [${NameDetails}!]! @relationship(type: "HAS_NAME", direction: OUT) } type ${BaseObject} @node @authorization(validate: [{ when: [BEFORE], where: { jwt: { roles_INCLUDES: "ALL" } } }]) @mutation(operations: []) { - id: ID! @id @unique + id: ID! @id } `; diff --git a/packages/graphql/tests/integration/issues/1782.int.test.ts b/packages/graphql/tests/integration/issues/1782.int.test.ts index c2015f75aa..31aae16286 100644 --- a/packages/graphql/tests/integration/issues/1782.int.test.ts +++ b/packages/graphql/tests/integration/issues/1782.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { const typeDefs = ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) @@ -51,15 +51,15 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! - nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [${testNameDetails}!]! @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } `; const extendedTypeDefs = ` type ${testMain} @node { - id: ID! @unique + id: ID! current: Boolean! main: [${testSeries}!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) } @@ -124,7 +124,7 @@ describe("https://github.com/neo4j/graphql/issues/1782", () => { node: { architectureConnection_SOME: { node: { - nameDetailsConnection: { + nameDetailsConnection_SOME: { node: { fullName_EQ: "MHA", }, diff --git a/packages/graphql/tests/integration/issues/1783.int.test.ts b/packages/graphql/tests/integration/issues/1783.int.test.ts index b65b02efd2..d4faf5e2ad 100644 --- a/packages/graphql/tests/integration/issues/1783.int.test.ts +++ b/packages/graphql/tests/integration/issues/1783.int.test.ts @@ -34,11 +34,11 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { const typeDefs = ` type ${testSeries} @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [${testMasterData}!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) - nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [${testNameDetails}!]! @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } type ${testNameDetails} @mutation(operations: []) @query(read: false, aggregate: false) @node { @@ -50,9 +50,9 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { } type ${testMasterData} @node { - id: ID! @unique + id: ID! current: Boolean! - nameDetails: ${testNameDetails} @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [${testNameDetails}!]! @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } `; await testHelper.initNeo4jGraphQL({ @@ -110,7 +110,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { const variableValues = { where: { current_EQ: true, - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { edge: { current_EQ: true, }, @@ -123,7 +123,7 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { current_EQ: true, }, node: { - nameDetailsConnection: { + nameDetailsConnection_SINGLE: { edge: { current_EQ: true, }, diff --git a/packages/graphql/tests/integration/issues/1817.int.test.ts b/packages/graphql/tests/integration/issues/1817.int.test.ts index e9e6d225d1..95a8773ba6 100644 --- a/packages/graphql/tests/integration/issues/1817.int.test.ts +++ b/packages/graphql/tests/integration/issues/1817.int.test.ts @@ -34,21 +34,21 @@ describe("https://github.com/neo4j/graphql/issues/1817", () => { const typeDefs = ` type ${TypeContainerType} @node { - id: ID! @id @unique + id: ID! @id name: String! specifiesContainers: [${TypeContainer}!]! @relationship(type: "hasContainer", properties: "CoT_Co_hasContainer", direction: OUT) } type ${TypeContainer} @node { - id: ID! @id @unique + id: ID! @id name: String containsMaterial: [${TypeMaterial}!]! @relationship(type: "hasMaterial", properties: "Co_Ma_hasMaterial", direction: OUT) } type ${TypeMaterial} @node { - id: ID! @id @unique + id: ID! @id name: String } diff --git a/packages/graphql/tests/integration/issues/1848.int.test.ts b/packages/graphql/tests/integration/issues/1848.int.test.ts index 033479934a..5fdd0b5895 100644 --- a/packages/graphql/tests/integration/issues/1848.int.test.ts +++ b/packages/graphql/tests/integration/issues/1848.int.test.ts @@ -33,17 +33,17 @@ describe("https://github.com/neo4j/graphql/issues/1848", () => { Community = testHelper.createUniqueType("Community"); const typeDefs = ` type ${ContentPiece} @node(labels: ["${ContentPiece}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type ${Project} @node(labels: ["${Project}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type ${Community} @node(labels: ["${Community}", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int hasContentPieces: [${ContentPiece}!]! @relationship(type: "COMMUNITY_CONTENTPIECE_HASCONTENTPIECES", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/1933.int.test.ts b/packages/graphql/tests/integration/issues/1933.int.test.ts index 34afc69339..1ee25ad404 100644 --- a/packages/graphql/tests/integration/issues/1933.int.test.ts +++ b/packages/graphql/tests/integration/issues/1933.int.test.ts @@ -32,7 +32,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { const typeDefs = ` type ${employeeType} @node { - employeeId: ID! @unique + employeeId: ID! firstName: String! @settable(onCreate: false, onUpdate: false) lastName: String @settable(onCreate: false, onUpdate: false) projects: [${projectType}!]! @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } type ${projectType} @node { - projectId: ID! @unique + projectId: ID! name: String! @settable(onCreate: false, onUpdate: false) description: String employees: [${employeeType}!]! diff --git a/packages/graphql/tests/integration/issues/200.int.test.ts b/packages/graphql/tests/integration/issues/200.int.test.ts index 93925efddb..d2f9072426 100644 --- a/packages/graphql/tests/integration/issues/200.int.test.ts +++ b/packages/graphql/tests/integration/issues/200.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/200", () => { test("should successfully execute given mutation", async () => { const typeDefs = ` type ${Category} @node { - categoryId: ID! @id @unique + categoryId: ID! @id name: String! description: String! @default(value: "") exampleImageLocations: [String!] diff --git a/packages/graphql/tests/integration/issues/2022.int.test.ts b/packages/graphql/tests/integration/issues/2022.int.test.ts deleted file mode 100644 index fac68c8bc3..0000000000 --- a/packages/graphql/tests/integration/issues/2022.int.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/2022", () => { - const testHelper = new TestHelper(); - - let ArtPiece: UniqueType; - let AuctionItem: UniqueType; - let Organization: UniqueType; - - beforeAll(async () => { - ArtPiece = testHelper.createUniqueType("ArtItem"); - AuctionItem = testHelper.createUniqueType("Auction"); - Organization = testHelper.createUniqueType("Organization"); - - const typeDefs = ` - type ${ArtPiece} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") - title: String! - auction: ${AuctionItem}! @relationship(type: "SOLD_AT_AUCTION_AS", direction: OUT) - owner: ${Organization}! @relationship(type: "OWNED_BY", direction: OUT) - } - - type ${AuctionItem} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") - auctionName: String! - lotNumber: Int! - - item: ${ArtPiece}! @relationship(type: "SOLD_AT_AUCTION_AS", direction: IN) - buyer: ${Organization}! @relationship(type: "BOUGHT_ITEM_AT_AUCTION", direction: IN) - seller: ${Organization}! @relationship(type: "SOLD_ITEM_AT_AUCTION", direction: IN) - } - - type ${Organization} @node { - dbId: ID! @id @unique @relayId @alias(property: "id") - name: String! - - artCollection: [${ArtPiece}!]! @relationship(type: "OWNED_BY", direction: IN) - itemsSoldAtAuction: [${AuctionItem}!]! @relationship(type: "SOLD_ITEM_AT_AUCTION", direction: OUT) - itemsBoughtAtAuction: [${AuctionItem}!]! @relationship(type: "BOUGHT_ITEM_AT_AUCTION", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not throw error when querying nested relations under a root connection field", async () => { - const query = ` - query { - ${ArtPiece.operations.connection} { - totalCount - edges { - node { - id - title - auction { - id - auctionName - lotNumber - buyer { - id - name - } - } - owner { - id - name - } - } - } - } - } - `; - - const queryResult = await testHelper.executeGraphQL(query); - expect(queryResult.errors).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/issues/2068.int.test.ts b/packages/graphql/tests/integration/issues/2068.int.test.ts deleted file mode 100644 index 6d69d18a4e..0000000000 --- a/packages/graphql/tests/integration/issues/2068.int.test.ts +++ /dev/null @@ -1,854 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createBearerToken } from "../../utils/create-bearer-token"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/pull/2068", () => { - const testHelper = new TestHelper(); - - beforeEach(() => {}); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Unions in cypher directives", async () => { - const actorName = "someName"; - const actorAge = 43; - const numberOfSeasons = 2; - const sharedTitle = "someTitle"; - - const actorType = testHelper.createUniqueType("Actor"); - const movieType = testHelper.createUniqueType("Movie"); - const tvShowType = testHelper.createUniqueType("TVShow"); - const movieOrTVShowType = testHelper.createUniqueType("MovieOrTVShow"); - - const typeDefs = ` - type ${actorType.name} @node { - name: String - age: Int - movies(title: String): [${movieType.name}] - @cypher( - statement: """ - MATCH (m:${movieType.name} {title: $title}) - RETURN m - """, - columnName: "m" - ) - - tvShows(title: String): [${movieType.name}] - @cypher( - statement: """ - MATCH (t:${tvShowType.name} {title: $title}) - RETURN t - """, - columnName: "t" - ) - - movieOrTVShow(title: String): [${movieOrTVShowType.name}] - @cypher( - statement: """ - MATCH (n) - WHERE (n:${tvShowType.name} OR n:${movieType.name}) AND ($title IS NULL OR n.title = $title) - RETURN n - """, - columnName: "n" - ) - } - - union ${movieOrTVShowType.name} = ${movieType.name} | ${tvShowType.name} - - type ${tvShowType.name} @node { - id: ID - title: String - numSeasons: Int - actors: [${actorType.name}] - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - topActor: ${actorType.name} - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - } - - type ${movieType.name} @node { - id: ID - title: String - actors: [${actorType.name}] - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - topActor: ${actorType.name} - @cypher( - statement: """ - MATCH (a:${actorType.name}) - RETURN a - """, - columnName: "a" - ) - } - `; - - const query = ` - { - ${actorType.plural} { - movieOrTVShow(title: "${sharedTitle}") { - ... on ${movieType.name} { - title - topActor { - name - age - } - } - ... on ${tvShowType.name} { - title - numSeasons - topActor { - name - } - } - } - } - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - await testHelper.executeCypher(` - CREATE (:${actorType.name} { name: "${actorName}", age: ${actorAge} }) - CREATE (:${tvShowType.name} { title: "${sharedTitle}", numSeasons: ${numberOfSeasons} }) - CREATE (:${movieType.name} { title: "${sharedTitle}" }) - `); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult.data as any)?.[actorType.plural]?.[0].movieOrTVShow).toIncludeSameMembers([ - { - title: sharedTitle, - numSeasons: numberOfSeasons, - topActor: { - name: actorName, - }, - }, - { - title: sharedTitle, - topActor: { - name: actorName, - age: actorAge, - }, - }, - ]); - }); - - describe("Updates within updates", () => { - let contentType: UniqueType; - let userType: UniqueType; - let commentType: UniqueType; - let typeDefs: string; - - const secret = "secret"; - - beforeEach(() => { - contentType = testHelper.createUniqueType("Content"); - userType = testHelper.createUniqueType("User"); - commentType = testHelper.createUniqueType("Comment"); - - typeDefs = ` - interface ${contentType.name} { - id: ID - content: String - creator: ${userType.name}! @declareRelationship - } - - type ${userType.name} @node { - id: ID - name: String - content: [${contentType.name}!]! @relationship(type: "HAS_CONTENT", direction: OUT) - } - - type ${commentType.name} implements ${contentType.name} @node { - id: ID - content: String - creator: ${userType.name}! @relationship(type: "HAS_CONTENT", direction: IN) - } - - extend type ${userType.name} - @authorization(filter: [{ operations: [READ, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } }]) - - extend type ${userType.name} @node { - password: String! @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) - } - `; - }); - - test("Connect node - update within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - const query = ` - mutation { - ${userType.operations.update}(update: { content: { connect: { where: { node: {} } } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"}) - CREATE (:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - - test("Connect node - user defined update where within an update", async () => { - const userID1 = "someID"; - const userID2 = "differentID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: { connect: { where: { node: { id_EQ: "${userID2}" } } } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID1}"}) - CREATE (:${userType.name} {id: "${userID2}"}) - CREATE (:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID1 }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID1, contentConnection: { totalCount: 0 } }]); - }); - - test("Disconnect node - update within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: { disconnect: { where: {} } } }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"})-[:HAS_CONTENT]->(:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - test("Disconnect node - user defined update where within an update", async () => { - const userID = "someID"; - const contentID = "someContentID"; - - const query = ` - mutation { - ${userType.operations.update}(update: { content: [{ disconnect: { where: { node: { id_EQ: "${userID}" } } } }] }) { - ${userType.plural} { - id - contentConnection { - totalCount - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs, features: { authorization: { key: secret } } }); - - await testHelper.executeCypher(` - CREATE (:${userType.name} {id: "${userID}"})-[:HAS_CONTENT]->(:${contentType.name} {id: "${contentID}"}) - `); - - const token = createBearerToken(secret, { sub: userID }); - - const gqlResult = await testHelper.executeGraphQLWithToken(query, token); - - expect(gqlResult.errors).toBeUndefined(); - - const users = (gqlResult.data as any)[userType.operations.update][userType.plural] as any[]; - expect(users).toEqual([{ id: userID, contentConnection: { totalCount: 0 } }]); - }); - }); - describe("connectOrCreate auth ordering", () => { - const secret = "secret"; - - const movieTitle = "Cool Movie"; - - const requiredRole = "admin"; - const forbiddenMessage = "Forbidden"; - const validToken = createBearerToken(secret, { roles: [requiredRole] }); - const invalidToken = createBearerToken(secret, { roles: [] }); - - /** - * Generate type definitions for connectOrCreate auth tests. - * @param operations The operations argument of auth rules. - * @returns Unique types and a graphql type deinition string. - */ - function getTypedef(operations: string): [UniqueType, UniqueType, string] { - const movieType = testHelper.createUniqueType("Movie"); - const genreType = testHelper.createUniqueType("Genre"); - const typeDefs = ` - type JWTPayload @jwt { - roles: [String!]! - } - - type ${movieType.name} @node { - title: String - genres: [${genreType.name}!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type ${genreType.name} @authorization(validate: [{ operations: ${operations}, where: { jwt: { roles_INCLUDES: "${requiredRole}" } } }]) @node { - name: String @unique - } - `; - - return [movieType, genreType, typeDefs]; - } - - /** - * Generate a query for connectOrCreate auth tests. - * @param mutationType The type of mutation to perform. - * @param movieTypePlural The plural of the movie type. - * @returns A graphql query. - */ - function getQuery(mutationType: string, movieTypePlural: string): string { - let argType = "update"; - - if (mutationType.startsWith("create")) { - argType = "input"; - } - - return /* GraphQL */ ` - mutation { - ${mutationType}( - ${argType}: { - title${mutationType.startsWith("update") ? "_SET" : ""}: "${movieTitle}" - genres: { - connectOrCreate: [ - { where: { node: { name_EQ: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ) { - ${movieTypePlural} { - title - } - } - } - `; - } - test("Create with createOrConnect and CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and CREATE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Create with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Create with createOrConnect and DELETE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[DELETE]"); - const createOperation = movieType.operations.create; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(createOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [createOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and CREATE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - - test("Update with createOrConnect and CREATE operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - test("Update with createOrConnect and CREATE, CREATE_RELATIONSHIP operation rule - invalid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[CREATE, CREATE_RELATIONSHIP]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - invalidToken - ); - - expect((gqlResult as any).errors[0].message as string).toBe(forbiddenMessage); - }); - test("Update with createOrConnect and DELETE operation rule - valid auth", async () => { - const [movieType, , typeDefs] = getTypedef("[DELETE]"); - const updateOperation = movieType.operations.update; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - await testHelper.executeCypher(`CREATE (:${movieType.name})`); - - const gqlResult = await testHelper.executeGraphQLWithToken( - getQuery(updateOperation, movieType.plural), - validToken - ); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [updateOperation]: { - [movieType.plural]: [ - { - title: movieTitle, - }, - ], - }, - }); - }); - }); - describe("Select connections following the creation of a multiple nodes", () => { - let movieType: UniqueType; - let actorType: UniqueType; - - let typeDefs: string; - - beforeEach(() => { - movieType = testHelper.createUniqueType("Movie"); - actorType = testHelper.createUniqueType("Actor"); - - typeDefs = ` - type ${movieType.name} @node { - title: String - actors: [${actorType.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${actorType.name} @node { - name: String - movies: [${movieType.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int - } - `; - }); - - test("Filtered results", async () => { - const filmName1 = "Forrest Gump"; - const filmName2 = "Toy Story"; - const actorName = "Tom Hanks"; - - const query = ` - mutation { - ${movieType.operations.create}(input: [{ title: "${filmName1}" }, { title: "${filmName2}" }]) { - ${movieType.plural} { - title - actorsConnection(where: { node: { name_EQ: "${actorName}" } }) { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [movieType.operations.create]: { - [movieType.plural]: [ - { - actorsConnection: { - edges: [], - }, - title: filmName1, - }, - { - actorsConnection: { - edges: [], - }, - title: filmName2, - }, - ], - }, - }); - }); - test("Unfiltered results", async () => { - const filmName1 = "Forrest Gump"; - const filmName2 = "Toy Story"; - - const query = ` - mutation { - ${movieType.operations.create}(input: [{ title: "${filmName1}" }, { title: "${filmName2}" }]) { - ${movieType.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [movieType.operations.create]: { - [movieType.plural]: [ - { - actorsConnection: { - edges: [], - }, - title: filmName1, - }, - { - actorsConnection: { - edges: [], - }, - title: filmName2, - }, - ], - }, - }); - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/2100.int.test.ts b/packages/graphql/tests/integration/issues/2100.int.test.ts index 62ee370e9f..0d60443993 100644 --- a/packages/graphql/tests/integration/issues/2100.int.test.ts +++ b/packages/graphql/tests/integration/issues/2100.int.test.ts @@ -57,7 +57,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { """, columnName: "markedAttendance" ) - serviceDate: ${TimeGraphType}! @relationship(type: "BUSSED_ON", direction: OUT) + serviceDate: [${TimeGraphType}!]! @relationship(type: "BUSSED_ON", direction: OUT) } interface Church { @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { } type ${BacentaType} implements Church @authentication @node { - id: ID @id @unique + id: ID @id name: String! serviceLogs: [${ServiceLogType}!]! @relationship(type: "HAS_HISTORY", direction: OUT) bussing(limit: Int!): [${BussingRecordType}!]! @@ -89,7 +89,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { id: ID! attendance: Int markedAttendance: Boolean! - serviceDate: ${TimeGraphType}! @declareRelationship + serviceDate: [${TimeGraphType}!]! @declareRelationship } `; diff --git a/packages/graphql/tests/integration/issues/2189.int.test.ts b/packages/graphql/tests/integration/issues/2189.int.test.ts index a468fe4654..714937f107 100644 --- a/packages/graphql/tests/integration/issues/2189.int.test.ts +++ b/packages/graphql/tests/integration/issues/2189.int.test.ts @@ -32,12 +32,12 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const typeDefs = ` type ${Test_Item} @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean - feedback: ${Test_Feedback} @relationship(type: "TEST_RELATIONSHIP", direction: IN) + feedback: [${Test_Feedback}!]! @relationship(type: "TEST_RELATIONSHIP", direction: IN) feedbackCypher: ${Test_Feedback} @cypher( statement: """ @@ -49,12 +49,12 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { ) } type ${Test_Feedback} @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean - item: ${Test_Item} @relationship(type: "TEST_RELATIONSHIP", direction: OUT) + item: [${Test_Item}!]! @relationship(type: "TEST_RELATIONSHIP", direction: OUT) } `; @@ -171,10 +171,10 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { expect(queryResult.errors).toBeFalsy(); expect((queryResult?.data as any)[Test_Item.plural]).toHaveLength(2); - expect((queryResult?.data as any)[Test_Item.plural].filter((t) => t.str === "one")[0].feedback.str).toBe( + expect((queryResult?.data as any)[Test_Item.plural].filter((t) => t.str === "one")[0].feedback[0].str).toBe( "hi there" ); - expect((queryResult?.data as any)[Test_Item.plural].filter((t) => t.str == "two")[0].feedback).toBeNull(); + expect((queryResult?.data as any)[Test_Item.plural].filter((t) => t.str == "two")[0].feedback).toBeEmpty(); }); test("Mutation with Cypher relationship in projection should return 2 nodes", async () => { @@ -269,11 +269,11 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { expect((result?.data as any)[Test_Item.operations.create][Test_Item.plural]).toHaveLength(2); expect( (result?.data as any)[Test_Item.operations.create][Test_Item.plural].filter((t) => t.str === "one")[0] - .feedback.str + .feedback[0].str ).toBe("hi there"); expect( (result?.data as any)[Test_Item.operations.create][Test_Item.plural].filter((t) => t.str == "two")[0] .feedback - ).toBeNull(); + ).toBeEmpty(); }); }); diff --git a/packages/graphql/tests/integration/issues/2249.int.test.ts b/packages/graphql/tests/integration/issues/2249.int.test.ts index aea32189f1..60d44f4d91 100644 --- a/packages/graphql/tests/integration/issues/2249.int.test.ts +++ b/packages/graphql/tests/integration/issues/2249.int.test.ts @@ -36,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { type ${Movie} @node { title: String! reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type Review @relationshipProperties { diff --git a/packages/graphql/tests/integration/issues/2250.int.test.ts b/packages/graphql/tests/integration/issues/2250.int.test.ts index 41beebaadb..380c188d22 100644 --- a/packages/graphql/tests/integration/issues/2250.int.test.ts +++ b/packages/graphql/tests/integration/issues/2250.int.test.ts @@ -22,14 +22,13 @@ import { TestHelper } from "../../utils/tests-helper"; describe("https://github.com/neo4j/graphql/issues/2250", () => { const testHelper = new TestHelper({ cdc: true }); - let cdcEnabled: boolean; let Movie: UniqueType; let Person: UniqueType; let Actor: UniqueType; beforeAll(async () => { - cdcEnabled = await testHelper.assertCDCEnabled(); + await testHelper.assertCDCEnabled(); }); beforeEach(async () => { @@ -37,7 +36,7 @@ describe("https://github.com/neo4j/graphql/issues/2250", () => { Person = testHelper.createUniqueType("Person"); Actor = testHelper.createUniqueType("Actor"); - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type ${Movie} @node { title: String! actors: [${Actor}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) @@ -78,10 +77,6 @@ describe("https://github.com/neo4j/graphql/issues/2250", () => { }); test("nested update with create while using subscriptions should generate valid Cypher", async () => { - if (!cdcEnabled) { - console.log("CDC NOT AVAILABLE - SKIPPING"); - return; - } const mutation = /* GraphQL */ ` mutation { ${Movie.operations.update}( diff --git a/packages/graphql/tests/integration/issues/2261.int.test.ts b/packages/graphql/tests/integration/issues/2261.int.test.ts index b685e451a4..acb8ea1305 100644 --- a/packages/graphql/tests/integration/issues/2261.int.test.ts +++ b/packages/graphql/tests/integration/issues/2261.int.test.ts @@ -43,15 +43,15 @@ describe("https://github.com/neo4j/graphql/issues/2261", () => { } type ${ProgrammeItem} implements Product @node { - id: ID! @id @unique + id: ID! @id uri: String! @cypher(statement: "RETURN 'example://programme-item/' + this.id as x", columnName: "x") editions: [${Edition}!]! @relationship(type: "HAS_EDITION", direction: OUT) } type ${Edition} @node { - id: ID! @id @unique + id: ID! @id uri: String! @cypher(statement: "RETURN 'example://edition/' + this.id as x", columnName: "x") - product: Product! @relationship(type: "HAS_EDITION", direction: IN) + product: [Product!]! @relationship(type: "HAS_EDITION", direction: IN) } `; @@ -96,9 +96,11 @@ describe("https://github.com/neo4j/graphql/issues/2261", () => { { id: "ed-id", uri: "example://edition/ed-id", - product: { - __typename: ProgrammeItem.name, - }, + product: [ + { + __typename: ProgrammeItem.name, + }, + ], }, ], }); diff --git a/packages/graphql/tests/integration/issues/2262.int.test.ts b/packages/graphql/tests/integration/issues/2262.int.test.ts index 00a31de33b..bfd1d54824 100644 --- a/packages/graphql/tests/integration/issues/2262.int.test.ts +++ b/packages/graphql/tests/integration/issues/2262.int.test.ts @@ -33,7 +33,7 @@ describe("https://github.com/neo4j/graphql/issues/2262", () => { const typeDefs = ` type ${Component} @node { uuid: String - upstreamProcess: ${Process} @relationship(type: "OUTPUT", direction: IN) + upstreamProcess: [${Process}!]! @relationship(type: "OUTPUT", direction: IN) downstreamProcesses: [${Process}!]! @relationship(type: "INPUT", direction: OUT) } diff --git a/packages/graphql/tests/integration/issues/227.int.test.ts b/packages/graphql/tests/integration/issues/227.int.test.ts index 7965052e38..ce4ba53f40 100644 --- a/packages/graphql/tests/integration/issues/227.int.test.ts +++ b/packages/graphql/tests/integration/issues/227.int.test.ts @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/227", () => { const typeDefs = /* GraphQL */ ` type ${Member} @node { id: ID! - gender: ${Gender}! @relationship(type: "HAS_GENDER", direction: OUT) + gender: [${Gender}!]! @relationship(type: "HAS_GENDER", direction: OUT) } type ${Gender} @node { @@ -100,6 +100,6 @@ describe("https://github.com/neo4j/graphql/issues/227", () => { expect(gqlResult.errors).toBeFalsy(); - expect((gqlResult?.data as any).townMemberList).toEqual([{ id: memberId, gender: { gender } }]); + expect((gqlResult?.data as any).townMemberList).toEqual([{ id: memberId, gender: [{ gender }] }]); }); }); diff --git a/packages/graphql/tests/integration/issues/235.int.test.ts b/packages/graphql/tests/integration/issues/235.int.test.ts index 2c18638600..f9032e1f3a 100644 --- a/packages/graphql/tests/integration/issues/235.int.test.ts +++ b/packages/graphql/tests/integration/issues/235.int.test.ts @@ -40,19 +40,19 @@ describe("https://github.com/neo4j/graphql/issues/235", () => { test("should create the correct number of nodes following multiple connect", async () => { const typeDefs = /* GraphQL */ ` type ${A} @node { - ID: ID! @id @unique + ID: ID! @id name: String rel_b: [${B}!]! @relationship(type: "REL_B", direction: OUT) rel_c: [${C}!]! @relationship(type: "REL_C", direction: OUT) } type ${B} @node { - ID: ID! @id @unique + ID: ID! @id name: String } type ${C} @node { - ID: ID! @id @unique + ID: ID! @id name: String } `; diff --git a/packages/graphql/tests/integration/issues/2388.int.test.ts b/packages/graphql/tests/integration/issues/2388.int.test.ts index a87b37f386..32e44ef8b5 100644 --- a/packages/graphql/tests/integration/issues/2388.int.test.ts +++ b/packages/graphql/tests/integration/issues/2388.int.test.ts @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2388", () => { { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } ]) { - id: ID! @id @unique + id: ID! @id } type ${PartUsage} @node @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/2388", () => { { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } ]) { - partAddress: ${PartAddress} + partAddress: [${PartAddress}!]! @relationship(type: "BELONGS_TO", direction: OUT) } @@ -92,7 +92,7 @@ describe("https://github.com/neo4j/graphql/issues/2388", () => { const query = ` query PartByNumber { ${Part.plural} { - partUsagesAggregate(where: { partAddress: { id_EQ: "123" } }) { + partUsagesAggregate(where: { partAddress_SOME: { id_EQ: "123" } }) { count } } diff --git a/packages/graphql/tests/integration/issues/2396.int.test.ts b/packages/graphql/tests/integration/issues/2396.int.test.ts index ceaa8bc0e6..2bf5846778 100644 --- a/packages/graphql/tests/integration/issues/2396.int.test.ts +++ b/packages/graphql/tests/integration/issues/2396.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { const typeDefs = ` type ${PostalCode} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! address: [${Address}!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } @@ -49,35 +49,35 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Address} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - postalCode: ${PostalCode}! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) + postalCode: [${PostalCode}!]! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) } extend type ${Address} @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) type ${Mandate} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: ID! @id @unique # numéro + number: ID! @id # numéro createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) price: Float! - valuation: ${Valuation}! @relationship(type: "HAS_VALUATION", direction: OUT) + valuation: [${Valuation}!]! @relationship(type: "HAS_VALUATION", direction: OUT) } extend type ${Mandate} @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) type ${Valuation} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - estate: ${Estate} @relationship(type: "VALUATION_FOR", direction: OUT) + estate: [${Estate}!]! @relationship(type: "VALUATION_FOR", direction: OUT) } extend type ${Valuation} @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) @@ -99,7 +99,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type ${Estate} @node @mutation(operations: [CREATE, UPDATE]) { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -107,7 +107,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { area: Float! floor: Int - address: ${Address} @relationship(type: "HAS_ADDRESS", direction: OUT) + address: [${Address}!]! @relationship(type: "HAS_ADDRESS", direction: OUT) } extend type ${Estate} @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) @@ -726,10 +726,10 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { offset: null, where: { price_GTE: 0, - valuation: { - estate: { - address: { - postalCode: { + valuation_SOME: { + estate_SOME: { + address_SOME: { + postalCode_SOME: { number_IN: ["13001"], }, }, @@ -767,10 +767,10 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: 20, where: { price_GTE: 0, - valuation: { - estate: { - address: { - postalCode: { + valuation_SOME: { + estate_SOME: { + address_SOME: { + postalCode_SOME: { number_IN: ["13001"], }, }, @@ -808,10 +808,10 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: 40, where: { price_GTE: 0, - valuation: { - estate: { - address: { - postalCode: { + valuation_SOME: { + estate_SOME: { + address_SOME: { + postalCode_SOME: { number_IN: ["13001"], }, }, diff --git a/packages/graphql/tests/integration/issues/2437.int.test.ts b/packages/graphql/tests/integration/issues/2437.int.test.ts index 6b3dd1e073..2a7ca788cd 100644 --- a/packages/graphql/tests/integration/issues/2437.int.test.ts +++ b/packages/graphql/tests/integration/issues/2437.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { } type ${Agent} @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime valuations: [${Valuation}!]! @relationship(type: "IS_VALUATION_AGENT", direction: OUT) @@ -47,10 +47,10 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { @authorization(validate: [{ operations: [CREATE], where: { jwt: { roles_INCLUDES: "Admin" } } }], filter: [{ where: { node: { archivedAt_EQ: null } } }]) type ${Valuation} @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime - agent: ${Agent}! @relationship(type: "IS_VALUATION_AGENT", direction: IN) + agent: [${Agent}!]! @relationship(type: "IS_VALUATION_AGENT", direction: IN) } extend type ${Valuation} @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) diff --git a/packages/graphql/tests/integration/issues/2474.int.test.ts b/packages/graphql/tests/integration/issues/2474.int.test.ts index f995d655c4..920f1d20f0 100644 --- a/packages/graphql/tests/integration/issues/2474.int.test.ts +++ b/packages/graphql/tests/integration/issues/2474.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { const typeDefs = ` type ${PostalCode.name} @node { archivedAt: DateTime - number: String! @unique + number: String! address: [${Address.name}!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } @@ -48,28 +48,28 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Address.name} @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - postalCode: ${PostalCode.name} @relationship(type: "HAS_POSTAL_CODE", direction: OUT) + postalCode: [${PostalCode.name}!]! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) node: [AddressNode!]! @relationship(type: "HAS_ADDRESS", direction: IN) } type ${Mandate.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: ID! @id @unique # numéro + number: ID! @id # numéro createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) price: Float! - valuation: ${Valuation.name}! @relationship(type: "HAS_VALUATION", direction: OUT) + valuation: [${Valuation.name}!]! @relationship(type: "HAS_VALUATION", direction: OUT) } type ${Valuation.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - estate: ${Estate.name} @relationship(type: "VALUATION_FOR", direction: OUT) + estate: [${Estate.name}!]! @relationship(type: "VALUATION_FOR", direction: OUT) } enum EstateType { @@ -89,13 +89,13 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { type ${Estate.name} @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) estateType: EstateType! area: Float! floor: Int - address: ${Address.name} @relationship(type: "HAS_ADDRESS", direction: OUT) + address: [${Address.name}!]! @relationship(type: "HAS_ADDRESS", direction: OUT) } `; @@ -172,21 +172,29 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { [Mandate.plural]: [ { price: 99000, - valuation: { - uuid: expect.any(String), - estate: { + valuation: [ + { uuid: expect.any(String), - area: 75, - estateType: "APARTMENT", - floor: 2, - address: { - uuid: expect.any(String), - postalCode: { - number: "13001", + estate: [ + { + uuid: expect.any(String), + area: 75, + estateType: "APARTMENT", + floor: 2, + address: [ + { + uuid: expect.any(String), + postalCode: [ + { + number: "13001", + }, + ], + }, + ], }, - }, + ], }, - }, + ], }, ], info: { @@ -291,26 +299,32 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { [Mandate.plural]: [ { price: 99000, - valuation: { - uuid: expect.any(String), - estate: { + valuation: [ + { uuid: expect.any(String), - area: 75, - estateType: "APARTMENT", - floor: 2, - address: { - uuid: expect.any(String), - node: expect.arrayContaining([ - { - area: 13.2, - }, - { - area: 75, - }, - ]), - }, + estate: [ + { + uuid: expect.any(String), + area: 75, + estateType: "APARTMENT", + floor: 2, + address: [ + { + uuid: expect.any(String), + node: expect.arrayContaining([ + { + area: 13.2, + }, + { + area: 75, + }, + ]), + }, + ], + }, + ], }, - }, + ], }, ], info: { diff --git a/packages/graphql/tests/integration/issues/2548.int.test.ts b/packages/graphql/tests/integration/issues/2548.int.test.ts index 8c624f9a4e..761e7fcead 100644 --- a/packages/graphql/tests/integration/issues/2548.int.test.ts +++ b/packages/graphql/tests/integration/issues/2548.int.test.ts @@ -44,7 +44,7 @@ describe("https://github.com/neo4j/graphql/issues/2548", () => { { operations: [READ], where: { jwt: { roles_INCLUDES: "ADMIN" } } } ] ) { - userId: ID! @id @unique + userId: ID! @id isPublic: Boolean } `; diff --git a/packages/graphql/tests/integration/issues/2574.int.test.ts b/packages/graphql/tests/integration/issues/2574.int.test.ts deleted file mode 100644 index a7644f585c..0000000000 --- a/packages/graphql/tests/integration/issues/2574.int.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/2574", () => { - const testHelper = new TestHelper(); - - let A: UniqueType; - let B: UniqueType; - let D: UniqueType; - - beforeEach(async () => { - A = testHelper.createUniqueType("A"); - B = testHelper.createUniqueType("B"); - D = testHelper.createUniqueType("D"); - - const typeDefs = ` - type ${A} @node { - uuid: ID! @id @unique - child: ${D}! @relationship(type: "HAS_PARENT", direction: IN) - } - - type ${B} @node { - uuid: ID! @id @unique - child: ${D}! @relationship(type: "HAS_PARENT", direction: IN) - } - - union C = ${A} | ${B} - - type ${D} @node { - uuid: ID! @id @unique - test: String! - parent: C! @relationship(type: "HAS_PARENT", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should create birectional union relationship without error", async () => { - const query = ` - mutation Mutation($input: [${A}CreateInput!]!) { - ${A.operations.create}(input: $input) { - ${A.plural} { - child { - test - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query, { - variableValues: { - input: { - child: { - create: { - node: { - test: "bla", - }, - }, - }, - }, - }, - }); - expect(result.errors).toBeFalsy(); - expect(result.data).toEqual({ - [A.operations.create]: { - [A.plural]: [ - { - child: { - test: "bla", - }, - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/2581.int.test.ts b/packages/graphql/tests/integration/issues/2581.int.test.ts index 901254647b..3931da7e63 100644 --- a/packages/graphql/tests/integration/issues/2581.int.test.ts +++ b/packages/graphql/tests/integration/issues/2581.int.test.ts @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { type ${Book} @node { name: String! year: Int - refID: ID @id @unique + refID: ID @id soldCopies: Int @cypher( statement: "OPTIONAL MATCH(sales:${Sales}) WHERE this.refID = sales.refID WITH count(sales) as result RETURN result as result" diff --git a/packages/graphql/tests/integration/issues/2630.int.test.ts b/packages/graphql/tests/integration/issues/2630.int.test.ts index a743235842..c4b4fc44dd 100644 --- a/packages/graphql/tests/integration/issues/2630.int.test.ts +++ b/packages/graphql/tests/integration/issues/2630.int.test.ts @@ -43,8 +43,8 @@ describe("https://github.com/neo4j/graphql/issues/2630", () => { } type ${Post} @node { - id: ID! @id @unique - subject: ${PostSubject}! @relationship(type: "POST_FOR", direction: OUT) + id: ID! @id + subject: [${PostSubject}!]! @relationship(type: "POST_FOR", direction: OUT) } type ${User} implements ${HasName} @node { @@ -98,7 +98,7 @@ describe("https://github.com/neo4j/graphql/issues/2630", () => { [Post.plural]: [ { id: postId, - subject: { name: userName }, + subject: [{ name: userName }], }, ], }); diff --git a/packages/graphql/tests/integration/issues/2709.int.test.ts b/packages/graphql/tests/integration/issues/2709.int.test.ts index 84f61f7324..e4c78e1b74 100644 --- a/packages/graphql/tests/integration/issues/2709.int.test.ts +++ b/packages/graphql/tests/integration/issues/2709.int.test.ts @@ -196,7 +196,7 @@ describe("https://github.com/neo4j/graphql/issues/2709 - extended", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") runtime: Int! distribution: [DistributionHouse!]! @relationship(type: "DISTRIBUTED_BY", direction: IN) - publisher: ${Publisher}! @relationship(type: "DISTRIBUTED_BY", direction: IN) + publisher: [${Publisher}!]! @relationship(type: "DISTRIBUTED_BY", direction: IN) } type ${Series} implements Production @node { diff --git a/packages/graphql/tests/integration/issues/2782.int.test.ts b/packages/graphql/tests/integration/issues/2782.int.test.ts index 119f75b288..b5bac3c271 100644 --- a/packages/graphql/tests/integration/issues/2782.int.test.ts +++ b/packages/graphql/tests/integration/issues/2782.int.test.ts @@ -48,7 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { type ${Photo} @node { id: ID! - color: ${Color} @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -190,15 +190,15 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { [Photo.plural]: expect.toIncludeSameMembers([ { id: "123", - color: null, + color: [], }, { id: "321", - color: null, + color: [], }, { id: "33211", - color: null, + color: [], }, ]), }); diff --git a/packages/graphql/tests/integration/issues/2812.int.test.ts b/packages/graphql/tests/integration/issues/2812.int.test.ts index 65a469b45f..ba19624c60 100644 --- a/packages/graphql/tests/integration/issues/2812.int.test.ts +++ b/packages/graphql/tests/integration/issues/2812.int.test.ts @@ -39,7 +39,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { } type ${Actor} @authorization(validate: [{ where: { node: { nodeCreatedBy_EQ: "$jwt.sub" } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String nodeCreatedBy: String fieldA: String @authorization(validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "role-A" } } }]) diff --git a/packages/graphql/tests/integration/issues/283.int.test.ts b/packages/graphql/tests/integration/issues/283.int.test.ts index a0651b5f35..4e34a1ed05 100644 --- a/packages/graphql/tests/integration/issues/283.int.test.ts +++ b/packages/graphql/tests/integration/issues/283.int.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/283", () => { } type ${Post} @node { - id: ID! @id @unique + id: ID! @id title: String! datetime: DateTime @timestamp(operations: [CREATE]) } diff --git a/packages/graphql/tests/integration/issues/2847.int.test.ts b/packages/graphql/tests/integration/issues/2847.int.test.ts deleted file mode 100644 index c03ba9b913..0000000000 --- a/packages/graphql/tests/integration/issues/2847.int.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/2847", () => { - const testHelper = new TestHelper(); - - let Movie: UniqueType; - let Actor: UniqueType; - let Product: UniqueType; - - beforeEach(async () => { - Movie = testHelper.createUniqueType("Movie"); - Actor = testHelper.createUniqueType("Actor"); - Product = testHelper.createUniqueType("Product"); - - const typeDefs = ` - interface ${Product} { - name: String! - } - - type ${Movie} implements ${Product} @node { - name: String! - } - - type ${Actor} @node { - name: String! - product: ${Product} @relationship(type: "HAS_PRODUCT", direction: OUT) - } - `; - - await testHelper.executeCypher( - ` - CREATE (c:${Actor}) - SET c.name = $name - `, - { name: "Keanu" } - ); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should returns actors even without any related product", async () => { - const query = ` - query { - ${Actor.plural} { - name - product { - name - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - expect(result.errors).toBeFalsy(); - expect(result.data).toEqual({ - [Actor.plural]: expect.toIncludeSameMembers([ - { - name: "Keanu", - product: null, - }, - ]), - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/2871.int.test.ts b/packages/graphql/tests/integration/issues/2871.int.test.ts index ac5dbed778..b9acc5f1cd 100644 --- a/packages/graphql/tests/integration/issues/2871.int.test.ts +++ b/packages/graphql/tests/integration/issues/2871.int.test.ts @@ -96,19 +96,19 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { const typeDefs = ` type ${FirstLevel} @node { - id: ID! @id @unique - secondLevel: ${SecondLevel}! @relationship(type: "HAS_SECOND_LEVEL", direction: OUT) + id: ID! @id + secondLevel: [${SecondLevel}!]! @relationship(type: "HAS_SECOND_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type ${SecondLevel} @node { - id: ID! @id @unique + id: ID! @id thirdLevel: [${ThirdLevel}!]! @relationship(type: "HAS_THIRD_LEVEL", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) } type ${ThirdLevel} @node { - id: ID! @id @unique + id: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) } `; @@ -125,7 +125,7 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { test("should be able to filter by SOME nested within single relationship", async () => { const query = ` query { - ${FirstLevel.plural}(where: { secondLevel: { thirdLevel_SOME: { id_EQ: "${thirdLevelInput3.id}" } } }) { + ${FirstLevel.plural}(where: { secondLevel_SINGLE: { thirdLevel_SOME: { id_EQ: "${thirdLevelInput3.id}" } } }) { id createdAt } @@ -142,7 +142,7 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { test("should be able to filter by ALL nested within single relationship", async () => { const queryExpectingEmptyList = ` query { - ${FirstLevel.plural}(where: { secondLevel: { thirdLevel_ALL: { id_EQ: "${thirdLevelInput3.id}" } } }) { + ${FirstLevel.plural}(where: { secondLevel_SINGLE: { thirdLevel_ALL: { id_EQ: "${thirdLevelInput3.id}" } } }) { id createdAt } @@ -151,7 +151,7 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { const queryExpectingData = ` query { - ${FirstLevel.plural}(where: { secondLevel: { thirdLevel_ALL: { id_EQ: "${thirdLevelInput1.id}" } } }) { + ${FirstLevel.plural}(where: { secondLevel_SINGLE: { thirdLevel_ALL: { id_EQ: "${thirdLevelInput1.id}" } } }) { id createdAt } @@ -178,7 +178,7 @@ describe("https://github.com/neo4j/graphql/issues/2871", () => { const query = ` query { - ${FirstLevel.plural}(where: { secondLevel: { thirdLevel_NONE: { id_EQ: "25" } } }) { + ${FirstLevel.plural}(where: { secondLevel_SINGLE: { thirdLevel_NONE: { id_EQ: "25" } } }) { id createdAt } diff --git a/packages/graphql/tests/integration/issues/2981.int.test.ts b/packages/graphql/tests/integration/issues/2981.int.test.ts deleted file mode 100644 index c3ce774c7b..0000000000 --- a/packages/graphql/tests/integration/issues/2981.int.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/2981", () => { - const testHelper = new TestHelper(); - - let Book: UniqueType; - let BookTitle_SV: UniqueType; - let BookTitle_EN: UniqueType; - - beforeEach(async () => { - Book = testHelper.createUniqueType("Book"); - BookTitle_SV = testHelper.createUniqueType("BookTitle_SV"); - BookTitle_EN = testHelper.createUniqueType("BookTitle_EN"); - - const typeDefs = ` - type ${Book} @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - union BookTitle = ${BookTitle_SV} | ${BookTitle_EN} - - type ${BookTitle_SV} @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type ${BookTitle_EN} @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should be able to create a nested translated title", async () => { - await testHelper.executeCypher(` - CREATE(book:${Book} {isbn: "123", originalTitle: "Original title"}) - `); - const query = ` - mutation UpdateBooks { - ${Book.operations.update}( - where: { isbn_EQ: "123" } - update: { translatedTitle: { ${BookTitle_EN}: { create: { node: { value: "English book title" } } } } } - ) { - ${Book.plural} { - isbn - originalTitle - translatedTitle { - ... on ${BookTitle_SV} { - value - } - ... on ${BookTitle_EN} { - value - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeFalsy(); - expect(result.data?.[Book.operations.update]).toEqual({ - [Book.plural]: expect.arrayContaining([ - { - isbn: "123", - originalTitle: "Original title", - translatedTitle: { - value: "English book title", - }, - }, - ]), - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/3027.int.test.ts b/packages/graphql/tests/integration/issues/3027.int.test.ts deleted file mode 100644 index d9863e6523..0000000000 --- a/packages/graphql/tests/integration/issues/3027.int.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/3027", () => { - describe("union", () => { - const testHelper = new TestHelper(); - - let Book: UniqueType; - let BookTitle_SV: UniqueType; - let BookTitle_EN: UniqueType; - - beforeEach(async () => { - Book = testHelper.createUniqueType("Book"); - BookTitle_SV = testHelper.createUniqueType("BookTitle_SV"); - BookTitle_EN = testHelper.createUniqueType("BookTitle_EN"); - - const typeDefs = ` - type ${Book} @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - union BookTitle = ${BookTitle_SV} | ${BookTitle_EN} - - type ${BookTitle_SV} @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type ${BookTitle_EN} @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should validate cardinality against all the implementations", async () => { - await testHelper.executeCypher(` - CREATE(:${Book} {isbn: "123", originalTitle: "Original title"})<-[:TRANSLATED_BOOK_TITLE]-(:${BookTitle_SV} { value: "Exempel på svensk titel"}) - `); - const query = ` - mutation UpdateBooks { - ${Book.operations.update}( - where: { isbn_EQ: "123" } - update: { translatedTitle: { ${BookTitle_EN}: { create: { node: { value: "English book title" } } } } } - ) { - ${Book.plural} { - isbn - originalTitle - translatedTitle { - ... on ${BookTitle_SV} { - value - } - ... on ${BookTitle_EN} { - value - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeTruthy(); - expect(result.errors).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - message: expect.stringContaining( - `Relationship field "${Book.name}.translatedTitle" cannot have more than one node linked` - ), - }), - ]) - ); - expect(result.data as any).toBeNull(); - }); - }); - - describe("interface", () => { - const testHelper = new TestHelper(); - - let Book: UniqueType; - let BookTitle_SV: UniqueType; - let BookTitle_EN: UniqueType; - - beforeEach(async () => { - Book = testHelper.createUniqueType("Book"); - BookTitle_SV = testHelper.createUniqueType("BookTitle_SV"); - BookTitle_EN = testHelper.createUniqueType("BookTitle_EN"); - - const typeDefs = ` - type ${Book} @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - interface BookTitle { - value: String! - } - - type ${BookTitle_SV} implements BookTitle @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type ${BookTitle_EN} implements BookTitle @node { - book: ${Book}! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should validate cardinality against all the implementations", async () => { - await testHelper.executeCypher(` - CREATE(book:${Book} {isbn: "123", originalTitle: "Original title"})<-[:TRANSLATED_BOOK_TITLE]-(:${BookTitle_SV} { value: "Exempel på svensk titel"}) - `); - const query = ` - mutation UpdateBooks { - ${Book.operations.update}( - where: { isbn_EQ: "123" } - update: { translatedTitle: { create: { node: { ${BookTitle_EN}: { value: "English book title" } } } - } } - ) { - ${Book.plural} { - isbn - originalTitle - translatedTitle { - ... on ${BookTitle_SV} { - value - } - ... on ${BookTitle_EN} { - value - } - } - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeTruthy(); - expect(result.errors).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - message: expect.stringContaining( - `Relationship field "${Book.name}.translatedTitle" cannot have more than one node linked` - ), - }), - ]) - ); - expect(result.data as any).toBeNull(); - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/3165.int.test.ts b/packages/graphql/tests/integration/issues/3165.int.test.ts index f00de7d8cd..790c8674f8 100644 --- a/packages/graphql/tests/integration/issues/3165.int.test.ts +++ b/packages/graphql/tests/integration/issues/3165.int.test.ts @@ -52,7 +52,7 @@ describe("https://github.com/neo4j/graphql/issues/3165", () => { type ${Related} @node { name: String! value: String! - target: RelatedTarget! + target: [RelatedTarget!]! @relationship(type: "PROPERTY_OF", properties: "RelatedProperties", direction: OUT) } `; @@ -97,8 +97,8 @@ describe("https://github.com/neo4j/graphql/issues/3165", () => { ${Related.plural}( where: { OR: [ - { targetConnection: { ${A}: { edge: { prop_EQ: "propvalue" } } } } - { targetConnection: { ${B}: { edge: { prop_EQ: "propvalue" } } } } + { targetConnection_SINGLE: { ${A}: { edge: { prop_EQ: "propvalue" } } } } + { targetConnection_SINGLE: { ${B}: { edge: { prop_EQ: "propvalue" } } } } ] } ) { diff --git a/packages/graphql/tests/integration/issues/3251.int.test.ts b/packages/graphql/tests/integration/issues/3251.int.test.ts deleted file mode 100644 index 00c9391db1..0000000000 --- a/packages/graphql/tests/integration/issues/3251.int.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/3251", () => { - const testHelper = new TestHelper(); - - let Movie: UniqueType; - let Genre: UniqueType; - - beforeEach(async () => { - Movie = testHelper.createUniqueType("Movie"); - Genre = testHelper.createUniqueType("Genre"); - - const typeDefs = `#graphql - type ${Movie} @node { - name: String! - genre: ${Genre}! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type ${Genre} @node { - name: String! @unique - movies: [${Movie}!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - await testHelper.executeCypher(` - CREATE (a:${Genre} { name: "Action" }) - CREATE (:${Genre} { name: "Thriller" }) - CREATE (:${Movie} { name: "TestMovie1" })-[:HAS_GENRE]->(a) - `); - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("Mutation which would violate 1:1 should throw error", async () => { - const mutation = `#graphql - mutation UpdateMovieWithConnectAndUpdate { - ${Movie.operations.update}( - where: { name_EQ: "TestMovie1" } - update: { name_SET: "TestMovie1", genre: { connect: { where: { node: { name_EQ: "Thriller" } } }} } - ) { - ${Movie.plural} { - name - genre { - name - } - } - } - } - `; - - const mutationResult = await testHelper.executeGraphQL(mutation); - - expect(mutationResult.errors).toHaveLength(1); - expect((mutationResult.errors as any)[0]?.message).toBe(`${Movie}.genre required exactly once`); - }); -}); diff --git a/packages/graphql/tests/integration/issues/3428.int.test.ts b/packages/graphql/tests/integration/issues/3428.int.test.ts index 884d5367f4..005e346b9e 100644 --- a/packages/graphql/tests/integration/issues/3428.int.test.ts +++ b/packages/graphql/tests/integration/issues/3428.int.test.ts @@ -38,10 +38,8 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { describe("Related to a concrete type", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -66,25 +64,6 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; updateMutationWithNestedCreate = `#graphql mutation { @@ -108,25 +87,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - connectOrCreate: { - where: { node: { id_EQ: "1" } } - onCreate: { node: { name: "someName" } } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -172,7 +133,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { test("Should not error and should only be able to perform the disconnect nested op when only the DISCONNECT nestedOperation is specified on rel to a type with a unique field", async () => { const typeDefs = `#graphql type ${Person} @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -185,14 +146,10 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -208,77 +165,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeFalsy(); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the disconnect and connectOrCreate nested ops when DISCONNECT and CONNECT_OR_CREATE are the only nestedOperations specified", async () => { - const typeDefs = `#graphql - type ${Person} @node { - id: ID! @id @unique - name: String - } - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [DISCONNECT, CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -287,7 +174,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' @@ -310,10 +197,8 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { let createMutationWithNestedCreate: string; let createMutationWithNestedConnect: string; - let createMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedCreate: string; let updateMutationWithNestedConnect: string; - let updateMutationWithNestedConnectOrCreate: string; let updateMutationWithNestedDisconnect: string; let updateMutationWithNestedUpdate: string; let updateMutationWithNestedDelete: string; @@ -341,27 +226,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { } } `; - createMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.create}( - input: { - id: "1" - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { name_EQ: "someName" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; + updateMutationWithNestedCreate = `#graphql mutation { ${Movie.operations.update}(update: { actors: { create: { node: { name: "someName" } } } }) { @@ -384,27 +249,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { } } `; - updateMutationWithNestedConnectOrCreate = `#graphql - mutation { - ${Movie.operations.update}( - update: { - actors: { - ${PersonOne}: { - connectOrCreate: { - where: { node: { name_EQ: "someName" } } - onCreate: { node: { name: "someName" } } - } - } - } - } - ) { - info { - nodesCreated - nodesDeleted - } - } - } - `; + updateMutationWithNestedDisconnect = `#graphql mutation { ${Movie.operations.update}( @@ -451,7 +296,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { test("Should not error and should only be able to perform the disconnect nested op when only the DISCONNECT nestedOperation is specified on rel to a type with a unique field", async () => { const typeDefs = `#graphql type ${PersonOne} @node { - name: String @unique + name: String } type ${PersonTwo} @node { @@ -469,14 +314,10 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); + const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); + const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( updateMutationWithNestedDisconnect @@ -492,82 +333,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "actors" is not defined by type' ); - expect(createWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((createWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "actors" is not defined by type' - ); - expect(updateWithNestedCreateResult.errors).toBeDefined(); - expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(updateWithNestedConnectResult.errors).toBeDefined(); - expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeDefined(); - expect((updateWithNestedConnectOrCreateResult.errors as any)[0].message).toInclude( - 'Field "connectOrCreate" is not defined by type' - ); - expect(updateWithNestedUpdateResult.errors).toBeDefined(); - expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( - 'Field "update" is not defined by type' - ); - expect(updateWithNestedDisconnectResult.errors).toBeFalsy(); - expect(updateWithNestedDeleteResult.errors).toBeDefined(); - expect((updateWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Field "delete" is not defined by type' - ); - expect(deleteWithNestedDeleteResult.errors).toBeDefined(); - expect((deleteWithNestedDeleteResult.errors as any)[0].message).toInclude( - 'Unknown argument "delete" on field' - ); - }); - - test("Should only be able to perform the disconnect and connectOrCreate nested ops when DISCONNECT and CONNECT_OR_CREATE are the only nestedOperations specified", async () => { - const typeDefs = `#graphql - type ${PersonOne} @node { - name: String @unique - } - - type ${PersonTwo} @node { - nameTwo: String - } - - union ${Person} = ${PersonOne} | ${PersonTwo} - - type ${Movie} @node { - id: ID - actors: [${Person}!]! @relationship(type: "ACTED_IN", direction: IN, nestedOperations: [DISCONNECT, CONNECT_OR_CREATE]) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const createWithNestedCreateResult = await testHelper.executeGraphQL(createMutationWithNestedCreate); - const createWithNestedConnectResult = await testHelper.executeGraphQL(createMutationWithNestedConnect); - const createWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - createMutationWithNestedConnectOrCreate - ); - const updateWithNestedCreateResult = await testHelper.executeGraphQL(updateMutationWithNestedCreate); - const updateWithNestedConnectResult = await testHelper.executeGraphQL(updateMutationWithNestedConnect); - const updateWithNestedConnectOrCreateResult = await testHelper.executeGraphQL( - updateMutationWithNestedConnectOrCreate - ); - const updateWithNestedUpdateResult = await testHelper.executeGraphQL(updateMutationWithNestedUpdate); - const updateWithNestedDisconnectResult = await testHelper.executeGraphQL( - updateMutationWithNestedDisconnect - ); - const updateWithNestedDeleteResult = await testHelper.executeGraphQL(updateMutationWithNestedDelete); - const deleteWithNestedDeleteResult = await testHelper.executeGraphQL(deleteMutationWithNestedDelete); - expect(createWithNestedCreateResult.errors).toBeDefined(); - expect((createWithNestedCreateResult.errors as any)[0].message).toInclude( - 'Field "create" is not defined by type' - ); - expect(createWithNestedConnectResult.errors).toBeDefined(); - expect((createWithNestedConnectResult.errors as any)[0].message).toInclude( - 'Field "connect" is not defined by type' - ); - expect(createWithNestedConnectOrCreateResult.errors).toBeFalsy(); expect(updateWithNestedCreateResult.errors).toBeDefined(); expect((updateWithNestedCreateResult.errors as any)[0].message).toInclude( 'Field "create" is not defined by type' @@ -576,7 +342,7 @@ describe("https://github.com/neo4j/graphql/issues/3428", () => { expect((updateWithNestedConnectResult.errors as any)[0].message).toInclude( 'Field "connect" is not defined by type' ); - expect(updateWithNestedConnectOrCreateResult.errors).toBeFalsy(); + expect(updateWithNestedUpdateResult.errors).toBeDefined(); expect((updateWithNestedUpdateResult.errors as any)[0].message).toInclude( 'Field "update" is not defined by type' diff --git a/packages/graphql/tests/integration/issues/350.int.test.ts b/packages/graphql/tests/integration/issues/350.int.test.ts index 5e6b2eeda6..6b5186bdbf 100644 --- a/packages/graphql/tests/integration/issues/350.int.test.ts +++ b/packages/graphql/tests/integration/issues/350.int.test.ts @@ -46,7 +46,7 @@ describe("https://github.com/neo4j/graphql/issues/350", () => { id: ID! flagged: Boolean! content: String! - post: ${Post}! @relationship(type: "HAS_COMMENT", direction: IN) + post: [${Post}!]! @relationship(type: "HAS_COMMENT", direction: IN) canEdit: Boolean! @cypher(statement: "RETURN false as res", columnName: "res") } `; diff --git a/packages/graphql/tests/integration/issues/354.int.test.ts b/packages/graphql/tests/integration/issues/354.int.test.ts deleted file mode 100644 index 9df88b0b66..0000000000 --- a/packages/graphql/tests/integration/issues/354.int.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { gql } from "graphql-tag"; -import { generate } from "randomstring"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/354", () => { - const testHelper = new TestHelper(); - - beforeAll(() => {}); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should throw when creating a node without a mandatory relationship", async () => { - const testComment = testHelper.createUniqueType("Comment"); - const testPost = testHelper.createUniqueType("Post"); - - const typeDefs = gql` - type ${testComment.name} @node { - comment_id: ID! - post: ${testPost.name}! @relationship(type: "HAS_POST", direction: OUT) - } - - type ${testPost.name} @node { - post_id: ID! - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const missingNodeId = generate({ - charset: "alphabetic", - }); - - const commentId = generate({ - charset: "alphabetic", - }); - - const query = ` - mutation { - ${testComment.operations.create}( - input: [{ - comment_id: "${commentId}", - post: { - connect: { - where: { node: { post_id_EQ: "${missingNodeId}" } } - } - } - }] - ) { - ${testComment.plural} { - comment_id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${testComment.name}.post required exactly once`); - }); -}); diff --git a/packages/graphql/tests/integration/issues/3888.int.test.ts b/packages/graphql/tests/integration/issues/3888.int.test.ts index 54e3242563..33e31d4810 100644 --- a/packages/graphql/tests/integration/issues/3888.int.test.ts +++ b/packages/graphql/tests/integration/issues/3888.int.test.ts @@ -38,10 +38,10 @@ describe("https://github.com/neo4j/graphql/issues/3888", () => { id: ID! } - type ${Post} @authorization(filter: [{ where: { node: { author: { id_EQ: "$jwt.sub" } } } }]) @node { + type ${Post} @authorization(filter: [{ where: { node: { author_SOME: { id_EQ: "$jwt.sub" } } } }]) @node { title: String! content: String! - author: ${User}! @relationship(type: "AUTHORED", direction: IN) + author: [${User}!]! @relationship(type: "AUTHORED", direction: IN) } `; @@ -101,9 +101,11 @@ describe("https://github.com/neo4j/graphql/issues/3888", () => { [Post.plural]: [ { title: "Test1", - author: { - id: "michel", - }, + author: [ + { + id: "michel", + }, + ], }, ], }, diff --git a/packages/graphql/tests/integration/issues/3901.int.test.ts b/packages/graphql/tests/integration/issues/3901.int.test.ts deleted file mode 100644 index 695658de6a..0000000000 --- a/packages/graphql/tests/integration/issues/3901.int.test.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createBearerToken } from "../../utils/create-bearer-token"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/3901", () => { - const testHelper = new TestHelper(); - - const secret = "secret"; - - let Serie: UniqueType; - let Season: UniqueType; - let User: UniqueType; - - beforeEach(async () => { - Serie = testHelper.createUniqueType("Serie"); - Season = testHelper.createUniqueType("Season"); - User = testHelper.createUniqueType("User"); - - const typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type ${User} @node { - id: ID! - series: [${Serie}!]! @relationship(type: "PUBLISHER", direction: OUT) - } - - type ${Serie} @node - @authorization( - validate: [ - { - operations: [CREATE] - when: [AFTER] - where: { - AND: [ - { node: { publisher: { id_EQ: "$jwt.sub" } } } - { jwt: { roles_INCLUDES: "verified" } } - { jwt: { roles_INCLUDES: "creator" } } - ] - } - } - ] - ) { - id: ID! @id - title: String! - - seasons: [${Season}!]! @relationship(type: "SEASON_OF", direction: IN) - publisher: ${User}! @relationship(type: "PUBLISHER", direction: IN) - } - - type ${Season} @node - @authorization( - validate: [ - { - operations: [CREATE] - when: [AFTER] - where: { - AND: [ - { node: { serie: { publisher: { id_EQ: "$jwt.sub" } } } } - { jwt: { roles_INCLUDES: "verified" } } - { jwt: { roles_INCLUDES: "creator" } } - ] - } - } - ] - ) { - id: ID! @id - number: Int! - serie: ${Serie}! @relationship(type: "SEASON_OF", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should not raise cardinality error when connecting on create", async () => { - const createUser = /* GraphQL */ ` - mutation { - ${User.operations.create}(input: [{ id: "michel" }]) { - ${User.plural} { - id - } - } - } - `; - - const createPost = /* GraphQL */ ` - mutation { - ${Serie.operations.create}( - input: [ - { - title: "title" - publisher: { connect: { where: { node: { id_EQ: "michel" } } } } - seasons: { create: { node: { number: 1 } } } - } - ] - ) { - ${Serie.plural} { - title - } - } - } - `; - - const token = createBearerToken(secret, { sub: "michel", roles: ["verified", "creator"] }); - - const createUserResult = await testHelper.executeGraphQLWithToken(createUser, token); - - expect(createUserResult.errors).toBeFalsy(); - - const createPostResult = await testHelper.executeGraphQLWithToken(createPost, token); - - expect(createPostResult.errors).toBeFalsy(); - expect(createPostResult.data).toEqual({ - [Serie.operations.create]: { - [Serie.plural]: [ - { - title: "title", - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/3929.int.test.ts b/packages/graphql/tests/integration/issues/3929.int.test.ts index 42654a10f8..63e4b8a6b2 100644 --- a/packages/graphql/tests/integration/issues/3929.int.test.ts +++ b/packages/graphql/tests/integration/issues/3929.int.test.ts @@ -41,23 +41,23 @@ describe("https://github.com/neo4j/graphql/issues/3929", () => { } type ${User} @authorization(filter: [{ where: { node: { id_EQ: "$jwt.uid" } } }]) @node { - id: ID! @unique + id: ID! email: String! name: String } - type ${Group} @authorization(validate: [{ where: { node: { creator: { id_EQ: "$jwt.uid" } } } }]) @node { - id: ID! @id @unique + type ${Group} @authorization(validate: [{ where: { node: { creator_SINGLE: { id_EQ: "$jwt.uid" } } } }]) @node { + id: ID! @id name: String members: [${Person}!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + creator: [${User}!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) } - type ${Person} @authorization(validate: [{ where: { node: { creator: { id_EQ: "$jwt.uid" }}}}]) @node { - id: ID! @id @unique + type ${Person} @authorization(validate: [{ where: { node: { creator_SINGLE: { id_EQ: "$jwt.uid" }}}}]) @node { + id: ID! @id name: String! - creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN) - group: ${Group}! @relationship(type: "MEMBER_OF", direction: OUT) + creator: [${User}!]! @relationship(type: "CREATOR_OF", direction: IN) + group: [${Group}!]! @relationship(type: "MEMBER_OF", direction: OUT) } extend schema @authentication @@ -146,7 +146,6 @@ describe("https://github.com/neo4j/graphql/issues/3929", () => { id_EQ: "user1_id", }, }, - overwrite: true, }, }, }, diff --git a/packages/graphql/tests/integration/issues/3938.int.test.ts b/packages/graphql/tests/integration/issues/3938.int.test.ts index 3a2eeb521b..a90f4d5d79 100644 --- a/packages/graphql/tests/integration/issues/3938.int.test.ts +++ b/packages/graphql/tests/integration/issues/3938.int.test.ts @@ -34,7 +34,7 @@ describe("https://github.com/neo4j/graphql/issues/3938", () => { const typeDefs = /* GraphQL */ ` type ${Group} @node { - id: ID! @id @unique + id: ID! @id name: String! invitees: [${Invitee}!]! @relationship(type: "INVITED_TO", direction: IN, aggregate: true) } @@ -47,11 +47,11 @@ describe("https://github.com/neo4j/graphql/issues/3938", () => { type ${Invitee} @node @authorization( validate: [ - { operations: [CREATE], where: { node: { group: { inviteesAggregate: { count_LT: 5 } } } } } + { operations: [CREATE], where: { node: { group_SINGLE: { inviteesAggregate: { count_LT: 5 } } } } } ] ) { - id: ID! @id @unique - group: ${Group}! @relationship(type: "INVITED_TO", direction: OUT) + id: ID! @id + group: [${Group}!]! @relationship(type: "INVITED_TO", direction: OUT) email: String! status: InviteeStatus! @default(value: PENDING) } diff --git a/packages/graphql/tests/integration/issues/402.int.test.ts b/packages/graphql/tests/integration/issues/402.int.test.ts deleted file mode 100644 index ad915c66a3..0000000000 --- a/packages/graphql/tests/integration/issues/402.int.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/402", () => { - const testHelper = new TestHelper(); - let Event: UniqueType; - let Area: UniqueType; - let typeDefs: string; - - beforeAll(() => { - Event = testHelper.createUniqueType("Event"); - Area = testHelper.createUniqueType("Area"); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should recreate test and return correct data", async () => { - typeDefs = ` - type ${Event} @node { - id: ID! - area: ${Area}! @relationship(type: "HAPPENS_IN", direction: OUT) - } - - type ${Area} @node { - id: ID! - } - `; - - const eventId = generate({ - charset: "alphabetic", - }); - - const areaId = generate({ - charset: "alphabetic", - }); - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - // testing the missing non non-null array - const query = ` - query ($area: [ID!]) { - ${Event.plural} ( - where: { - id_EQ: "${eventId}" - area: { - id_IN: $area - } - } - ) - { - id - area { - id - } - } - } - `; - - await testHelper.executeCypher( - ` - CREATE (:${Event} {id: $eventId})-[:HAPPENS_IN]->(:${Area} {id: $areaId}) - `, - { eventId, areaId } - ); - - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - - expect(gqlResult.data as any).toEqual({ - [Event.plural]: [ - { - id: eventId, - area: { id: areaId }, - }, - ], - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/4056.int.test.ts b/packages/graphql/tests/integration/issues/4056.int.test.ts index 0191304367..4517c1408f 100644 --- a/packages/graphql/tests/integration/issues/4056.int.test.ts +++ b/packages/graphql/tests/integration/issues/4056.int.test.ts @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/4056", () => { { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT) } @@ -81,14 +81,14 @@ describe("https://github.com/neo4j/graphql/issues/4056", () => { ] ) { id: ID! @id - settings: ${Settings}! @relationship(type: "HAS_SETTINGS", direction: OUT) + settings: [${Settings}!]! @relationship(type: "HAS_SETTINGS", direction: OUT) admins: [${User}!]! @relationship(type: "ADMIN_IN", direction: IN) } type ${Settings} @node { id: ID! @id - tenant: ${Tenant}! @relationship(type: "HAS_SETTINGS", direction: IN) + tenant: [${Tenant}!]! @relationship(type: "HAS_SETTINGS", direction: IN) openingDays: [${OpeningDay}!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) name: String updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) @@ -98,12 +98,12 @@ describe("https://github.com/neo4j/graphql/issues/4056", () => { @node @authorization( validate: [ - { where: { node: {settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } + { where: { node: {settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { id: ID! @id - settings: ${Settings} @relationship(type: "VALID_OPENING_DAYS", direction: IN) + settings: [${Settings}!]! @relationship(type: "VALID_OPENING_DAYS", direction: IN) name: String updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } diff --git a/packages/graphql/tests/integration/issues/4077.int.test.ts b/packages/graphql/tests/integration/issues/4077.int.test.ts index cba4ed23cd..5f48939c40 100644 --- a/packages/graphql/tests/integration/issues/4077.int.test.ts +++ b/packages/graphql/tests/integration/issues/4077.int.test.ts @@ -42,13 +42,13 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { type ${PreviewClip} @mutation(operations: [DELETE]) @node { id: ID! @id markedAsDone: Boolean! @default(value: false) - clippedFrom: ${Video}! @relationship(type: "VIDEO_HAS_PREVIEW_CLIP", direction: IN) + clippedFrom: [${Video}!]! @relationship(type: "VIDEO_HAS_PREVIEW_CLIP", direction: IN) } extend type ${PreviewClip} @authorization( filter: [ - { where: { node: { clippedFrom: { publisher: { id_EQ: "$jwt.sub" } } } } } + { where: { node: { clippedFrom_SOME: { publisher_SOME: { id_EQ: "$jwt.sub" } } } } } { where: { jwt: { roles_INCLUDES: "admin" } } } ] ) @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { type ${Video} @mutation(operations: [UPDATE]) @node { id: ID! @id - publisher: ${User}! @relationship(type: "PUBLISHER", direction: IN) + publisher: [${User}!]! @relationship(type: "PUBLISHER", direction: IN) processing: String! clips: [${PreviewClip}!]! @relationship(type: "VIDEO_HAS_PREVIEW_CLIP", direction: OUT) @@ -65,7 +65,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { extend type ${Video} @authorization( filter: [ - { where: { node: { publisher: { id_EQ: "$jwt.sub" } } } } + { where: { node: { publisher_SOME: { id_EQ: "$jwt.sub" } } } } { where: { jwt: { roles_INCLUDES: "admin" } } } { requireAuthentication: false @@ -105,7 +105,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { test("get clips with correct filters", async () => { const query = /* GraphQL */ ` query { - ${PreviewClip.plural}(where: { clippedFrom: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { + ${PreviewClip.plural}(where: { clippedFrom_SOME: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { id } } @@ -135,7 +135,7 @@ describe("https://github.com/neo4j/graphql/issues/4077", () => { const query = /* GraphQL */ ` query { ${Video.plural} { - clips(where: { clippedFrom: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { + clips(where: { clippedFrom_SOME: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { id } } diff --git a/packages/graphql/tests/integration/issues/4110.int.test.ts b/packages/graphql/tests/integration/issues/4110.int.test.ts index 6313232313..f9de10b2d4 100644 --- a/packages/graphql/tests/integration/issues/4110.int.test.ts +++ b/packages/graphql/tests/integration/issues/4110.int.test.ts @@ -37,14 +37,14 @@ describe("https://github.com/neo4j/graphql/issues/4110", () => { const typeDefs = /* GraphQL */ ` type ${Company} @node @authorization( - filter: [{ operations: [READ], where: { node: { inBetween: { company: { id_EQ: "example" } } } } }] + filter: [{ operations: [READ], where: { node: { inBetween_SOME: { company_SOME: { id_EQ: "example" } } } } }] ) { id: ID @id - inBetween: ${InBetween} @relationship(type: "CONNECT_TO", direction: OUT) + inBetween: [${InBetween}!]! @relationship(type: "CONNECT_TO", direction: OUT) } type ${InBetween} @node { id: ID @id - company: ${Company}! @relationship(type: "CONNECT_TO", direction: IN) + company: [${Company}!]! @relationship(type: "CONNECT_TO", direction: IN) } `; @@ -97,11 +97,15 @@ describe("https://github.com/neo4j/graphql/issues/4110", () => { expect((result.data as any)[Company.plural]).toEqual([ { id: "example", - inBetween: { - company: { - id: "example", + inBetween: [ + { + company: [ + { + id: "example", + }, + ], }, - }, + ], }, ]); }); diff --git a/packages/graphql/tests/integration/issues/4112.int.test.ts b/packages/graphql/tests/integration/issues/4112.int.test.ts index 630f9b6364..238afb1fc0 100644 --- a/packages/graphql/tests/integration/issues/4112.int.test.ts +++ b/packages/graphql/tests/integration/issues/4112.int.test.ts @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; @@ -93,7 +93,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; @@ -139,7 +139,7 @@ describe("https://github.com/neo4j/graphql/issues/4112", () => { } type ${Category} @authentication(operations: [READ], jwt: { roles_INCLUDES: "admin" }) @node { - name: String! @unique + name: String! } `; diff --git a/packages/graphql/tests/integration/issues/4113.int.test.ts b/packages/graphql/tests/integration/issues/4113.int.test.ts index 2eb380a139..4999eb6bde 100644 --- a/packages/graphql/tests/integration/issues/4113.int.test.ts +++ b/packages/graphql/tests/integration/issues/4113.int.test.ts @@ -44,28 +44,28 @@ describe("https://github.com/neo4j/graphql/issues/4113", () => { } type ${User.name} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! - store: ${Store.name} @relationship(type: "WORKS_AT", direction: OUT) + store: [${Store.name}!]! @relationship(type: "WORKS_AT", direction: OUT) } type ${Store.name} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User.name}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction.name}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction.name} @node { - id: ID! @id @unique - store: ${Store.name}! @relationship(type: "TRANSACTION", direction: OUT) + id: ID! @id + store: [${Store.name}!]! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [${TransactionItem.name}!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) } type ${TransactionItem.name} @node { - transaction: ${Transaction.name} @relationship(type: "ITEM_TRANSACTED", direction: OUT) + transaction: [${Transaction.name}!]! @relationship(type: "ITEM_TRANSACTED", direction: OUT) name: String price: Float quantity: Int @@ -79,7 +79,7 @@ describe("https://github.com/neo4j/graphql/issues/4113", () => { operations: [CREATE, CREATE_RELATIONSHIP] where: { jwt: { OR: [{ roles_INCLUDES: "store-owner" }, { roles_INCLUDES: "employee" }] } - node: { store: { id_EQ: "$jwt.store" } } + node: { store_SINGLE: { id_EQ: "$jwt.store" } } } } ] @@ -93,7 +93,7 @@ describe("https://github.com/neo4j/graphql/issues/4113", () => { operations: [CREATE, CREATE_RELATIONSHIP] where: { jwt: { OR: [{ roles_INCLUDES: "store-owner" }, { roles_INCLUDES: "employee" }] } - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + node: { transaction_SINGLE: { store_SINGLE: { id_EQ: "$jwt.store" } } } } } ] @@ -200,9 +200,11 @@ describe("https://github.com/neo4j/graphql/issues/4113", () => { [Transaction.operations.create]: { [Transaction.plural]: [ { - store: { - name: "Store", - }, + store: [ + { + name: "Store", + }, + ], items: expect.toIncludeSameMembers([ { name: "Milk", @@ -243,42 +245,42 @@ describe("replicates the test for relationship to interface so that multiple ref } type ${User.name} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! - store: ${Store.name} @relationship(type: "WORKS_AT", direction: OUT) + store: [${Store.name}!]! @relationship(type: "WORKS_AT", direction: OUT) } type ${Store.name} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User.name}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction.name}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction.name} @node { - id: ID! @id @unique - store: ${Store.name}! @relationship(type: "TRANSACTION", direction: OUT) + id: ID! @id + store: [${Store.name}!]! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [TransactionItemI!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) } interface TransactionItemI { - transaction: ${Transaction.name} @declareRelationship + transaction: [${Transaction.name}!]! @declareRelationship name: String price: Float quantity: Int } type ${TransactionItem1.name} implements TransactionItemI @node { - transaction: ${Transaction.name} @relationship(type: "ITEM_TRANSACTED", direction: OUT) + transaction: [${Transaction.name}!]! @relationship(type: "ITEM_TRANSACTED", direction: OUT) name: String price: Float quantity: Int } type ${TransactionItem2.name} implements TransactionItemI @node { - transaction: ${Transaction.name} @relationship(type: "ITEM_TRANSACTED", direction: OUT) + transaction: [${Transaction.name}!]! @relationship(type: "ITEM_TRANSACTED", direction: OUT) name: String price: Float quantity: Int @@ -292,7 +294,7 @@ describe("replicates the test for relationship to interface so that multiple ref operations: [CREATE, CREATE_RELATIONSHIP] where: { jwt: { OR: [{ roles_INCLUDES: "store-owner" }, { roles_INCLUDES: "employee" }] } - node: { store: { id_EQ: "$jwt.store" } } + node: { store_SINGLE: { id_EQ: "$jwt.store" } } } } ] @@ -306,7 +308,7 @@ describe("replicates the test for relationship to interface so that multiple ref operations: [CREATE, CREATE_RELATIONSHIP] where: { jwt: { OR: [{ roles_INCLUDES: "store-owner" }, { roles_INCLUDES: "employee" }] } - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + node: { transaction_SINGLE: { store_SINGLE: { id_EQ: "$jwt.store" } } } } } ] @@ -320,7 +322,7 @@ describe("replicates the test for relationship to interface so that multiple ref operations: [CREATE, CREATE_RELATIONSHIP] where: { jwt: { OR: [{ roles_INCLUDES: "store-owner" }, { roles_INCLUDES: "employee" }] } - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + node: { transaction_SINGLE: { store_SINGLE: { id_EQ: "$jwt.store" } } } } } ] @@ -432,9 +434,11 @@ describe("replicates the test for relationship to interface so that multiple ref [Transaction.operations.create]: { [Transaction.plural]: [ { - store: { - name: "Store", - }, + store: [ + { + name: "Store", + }, + ], items: expect.toIncludeSameMembers([ { name: "Milk", diff --git a/packages/graphql/tests/integration/issues/4115.int.test.ts b/packages/graphql/tests/integration/issues/4115.int.test.ts index b884ee23d3..b8ca66e5a4 100644 --- a/packages/graphql/tests/integration/issues/4115.int.test.ts +++ b/packages/graphql/tests/integration/issues/4115.int.test.ts @@ -36,14 +36,14 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { const typeDefs = ` type ${User} @node { - id: ID! @unique + id: ID! roles: [String!]! } type ${Family} @node { - id: ID! @id @unique + id: ID! @id members: [${Person}!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN) + creator: [${User}!]! @relationship(type: "CREATOR_OF", direction: IN) } type ${Person} @node @@ -52,16 +52,16 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { { where: { AND: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { family: { creator: { roles_INCLUDES: "plan:paid" } } } } + { node: { creator_SINGLE: { id_EQ: "$jwt.uid" } } } + { node: { family_SINGLE: { creator_SINGLE: { roles_INCLUDES: "plan:paid" } } } } ] } } ] ) { - id: ID! @id @unique - creator: ${User}! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) - family: ${Family}! @relationship(type: "MEMBER_OF", direction: OUT) + id: ID! @id + creator: [${User}!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + family: [${Family}!]! @relationship(type: "MEMBER_OF", direction: OUT) } type JWT @jwt { diff --git a/packages/graphql/tests/integration/issues/4118.int.test.ts b/packages/graphql/tests/integration/issues/4118.int.test.ts index 30ad5ea229..172522a715 100644 --- a/packages/graphql/tests/integration/issues/4118.int.test.ts +++ b/packages/graphql/tests/integration/issues/4118.int.test.ts @@ -65,7 +65,7 @@ describe("https://github.com/neo4j/graphql/issues/4118", () => { { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } @@ -78,7 +78,7 @@ describe("https://github.com/neo4j/graphql/issues/4118", () => { ] ) { id: ID! @id - settings: ${Settings.name}! @relationship(type: "HAS_SETTINGS", direction: OUT) + settings: [${Settings.name}!]! @relationship(type: "HAS_SETTINGS", direction: OUT) admins: [${User.name}!]! @relationship(type: "ADMIN_IN", direction: IN) } @@ -86,12 +86,12 @@ describe("https://github.com/neo4j/graphql/issues/4118", () => { @node @authorization( validate: [ - { where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } + { where: { node: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) { id: ID! @id - tenant: ${Tenant.name}! @relationship(type: "HAS_SETTINGS", direction: IN) + tenant: [${Tenant.name}!]! @relationship(type: "HAS_SETTINGS", direction: IN) openingDays: [${OpeningDay.name}!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) name: String } @@ -99,15 +99,15 @@ describe("https://github.com/neo4j/graphql/issues/4118", () => { type ${OpeningDay.name} @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [{ where: { node: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] ) { - settings: ${Settings.name} @relationship(type: "VALID_OPENING_DAYS", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VALID_OPENING_DAYS", direction: IN) id: ID! @id name: String } - type ${LOL.name} @authorization(validate: [{ where: { node: { host: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { - host: ${Tenant.name}! @relationship(type: "HOSTED_BY", direction: OUT) + type ${LOL.name} @authorization(validate: [{ where: { node: { host_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { + host: [${Tenant.name}!]! @relationship(type: "HOSTED_BY", direction: OUT) openingDays: [${OpeningDay.name}!]! @relationship(type: "HAS_OPENING_DAY", direction: OUT) } `; diff --git a/packages/graphql/tests/integration/issues/413.int.test.ts b/packages/graphql/tests/integration/issues/413.int.test.ts index af18f5aa7c..7012603e61 100644 --- a/packages/graphql/tests/integration/issues/413.int.test.ts +++ b/packages/graphql/tests/integration/issues/413.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/413", () => { } type ${JobPlan} @node { - id: ID! @id @unique + id: ID! @id tenantID: ID! name: String! } diff --git a/packages/graphql/tests/integration/issues/4170.int.test.ts b/packages/graphql/tests/integration/issues/4170.int.test.ts index 18b15c4cda..9a1f20ea43 100644 --- a/packages/graphql/tests/integration/issues/4170.int.test.ts +++ b/packages/graphql/tests/integration/issues/4170.int.test.ts @@ -58,19 +58,19 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } type ${Tenant.name} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { id: ID! @id - settings: ${Settings.name}! @relationship(type: "HAS_SETTINGS", direction: OUT) + settings: [${Settings.name}!]! @relationship(type: "HAS_SETTINGS", direction: OUT) admins: [${User.name}!]! @relationship(type: "ADMIN_IN", direction: IN) } - type ${Settings.name} @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { + type ${Settings.name} @authorization(validate: [{ where: { node: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { id: ID! @id - tenant: ${Tenant.name}! @relationship(type: "HAS_SETTINGS", direction: IN) + tenant: [${Tenant.name}!]! @relationship(type: "HAS_SETTINGS", direction: IN) openingDays: [${OpeningDay.name}!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) name: String } @@ -78,10 +78,10 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { type ${OpeningDay.name} @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [{ where: { node: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] ) { id: ID! @id - settings: ${Settings.name} @relationship(type: "VALID_GARAGES", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VALID_GARAGES", direction: IN) open: [${OpeningHoursInterval.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) } @@ -89,11 +89,11 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { @node @authorization( validate: [ - { where: { node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } + { where: { node: { openingDay_SOME: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } ] ) { name: String - openingDay: ${OpeningDay.name}! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) + openingDay: [${OpeningDay.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } `; diff --git a/packages/graphql/tests/integration/issues/4214.int.test.ts b/packages/graphql/tests/integration/issues/4214.int.test.ts index 806ac2fd17..8c61ca78d5 100644 --- a/packages/graphql/tests/integration/issues/4214.int.test.ts +++ b/packages/graphql/tests/integration/issues/4214.int.test.ts @@ -44,29 +44,29 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { } type ${User} @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! - store: ${Store} @relationship(type: "WORKS_AT", direction: OUT) + store: [${Store}!]! @relationship(type: "WORKS_AT", direction: OUT) } type ${Store} @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [${User}!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [${Transaction}!]! @relationship(type: "TRANSACTION", direction: IN) } type ${Transaction} @node { - id: ID! @id @unique - store: ${Store}! @relationship(type: "TRANSACTION", direction: OUT) + id: ID! @id + store: [${Store}!]! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [${TransactionItem}!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) completed: Boolean } type ${TransactionItem} @node { - transaction: ${Transaction} @relationship(type: "ITEM_TRANSACTED", direction: OUT) + transaction: [${Transaction}!]! @relationship(type: "ITEM_TRANSACTED", direction: OUT) name: String price: Float quantity: Int @@ -84,7 +84,7 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { jwt: { roles_INCLUDES: "store-owner" } } { jwt: { roles_INCLUDES: "employee" } } ] - node: { store: { id_EQ: "$jwt.store" } } + node: { store_SOME: { id_EQ: "$jwt.store" } } } } ] @@ -99,7 +99,7 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { jwt: { roles_INCLUDES: "store-owner" } } { jwt: { roles_INCLUDES: "employee" } } ] - node: { store: { id_EQ: "$jwt.store" } } + node: { store_SOME: { id_EQ: "$jwt.store" } } } } ] @@ -117,7 +117,7 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { jwt: { roles_INCLUDES: "store-owner" } } { jwt: { roles_INCLUDES: "employee" } } ] - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + node: { transaction_SOME: { store_SOME: { id_EQ: "$jwt.store" } } } } } ] @@ -132,7 +132,7 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { jwt: { roles_INCLUDES: "store-owner" } } { jwt: { roles_INCLUDES: "employee" } } ] - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + node: { transaction_SOME: { store_SOME: { id_EQ: "$jwt.store" } } } } } ] diff --git a/packages/graphql/tests/integration/issues/4223.int.test.ts b/packages/graphql/tests/integration/issues/4223.int.test.ts index 873d1d17c3..a2e9d485fc 100644 --- a/packages/graphql/tests/integration/issues/4223.int.test.ts +++ b/packages/graphql/tests/integration/issues/4223.int.test.ts @@ -58,30 +58,30 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } type ${Tenant.name} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { id: ID! @id - settings: ${Settings.name}! @relationship(type: "VEHICLECARD_OWNER", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VEHICLECARD_OWNER", direction: IN) admins: [${User.name}!]! @relationship(type: "ADMIN_IN", direction: IN) } - type ${Settings.name} @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { + type ${Settings.name} @authorization(validate: [{ where: { node: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { id: ID! @id openingDays: [${OpeningDay.name}!]! @relationship(type: "VALID_GARAGES", direction: OUT) - myWorkspace: ${MyWorkspace.name}! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: OUT) - tenant: ${Tenant.name}! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) + myWorkspace: [${MyWorkspace.name}!]! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: OUT) + tenant: [${Tenant.name}!]! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) } type ${OpeningDay.name} @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [{ where: { node: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] ) { id: ID! @id - settings: ${Settings.name} @relationship(type: "VALID_GARAGES", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VALID_GARAGES", direction: IN) open: [${OpeningHoursInterval.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) } @@ -89,11 +89,11 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { @node @authorization( validate: [ - { where: { node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } + { where: { node: { openingDay_SOME: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } ] ) { name: String - openingDay: ${OpeningDay.name}! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) + openingDay: [${OpeningDay.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) } type ${MyWorkspace.name} @@ -103,13 +103,13 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { { where: { node: { - settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } + settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } ] ) { - settings: ${Settings.name}! + settings: [${Settings.name}!]! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: IN) workspace: String updatedBy: String diff --git a/packages/graphql/tests/integration/issues/4292.int.test.ts b/packages/graphql/tests/integration/issues/4292.int.test.ts index 9357bcee1d..35014d11a8 100644 --- a/packages/graphql/tests/integration/issues/4292.int.test.ts +++ b/packages/graphql/tests/integration/issues/4292.int.test.ts @@ -44,8 +44,8 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${User.name} @node { - id: ID! @unique - email: String! @unique + id: ID! + email: String! name: String creator: [${Group.name}!]! @relationship(type: "CREATOR_OF", direction: OUT) admin: [${Admin.name}!]! @relationship(type: "IS_USER", direction: IN) @@ -55,10 +55,10 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type ${Group.name} @node { - id: ID! @id @unique + id: ID! @id name: String members: [${Person.name}!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: ${User.name}! + creator: [${User.name}!]! @relationship(type: "CREATOR_OF", direction: IN) @settable(onCreate: true, onUpdate: true) admins: [${Admin.name}!]! @relationship(type: "ADMIN_OF", direction: IN) @@ -70,15 +70,15 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { validate: [ { operations: [CREATE] - where: { node: { group: { creator: { roles_INCLUDES: "plan:paid" } } } } + where: { node: { group_SOME: { creator_SOME: { roles_INCLUDES: "plan:paid" } } } } } { operations: [DELETE] where: { OR: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { group: { admins_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { creator: { id_EQ: "$jwt.uid" } } } } + { node: { creator_SOME: { id_EQ: "$jwt.uid" } } } + { node: { group_SOME: { admins_SOME: { user_SOME: { id_EQ: "$jwt.uid" } } } } } + { node: { group_SOME: { creator_SOME: { id_EQ: "$jwt.uid" } } } } ] } } @@ -86,25 +86,25 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { operations: [READ, UPDATE] where: { OR: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { group: { admins_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { contributors_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { creator: { id_EQ: "$jwt.uid" } } } } + { node: { creator_SOME: { id_EQ: "$jwt.uid" } } } + { node: { group_SOME: { admins_SOME: { user_SOME: { id_EQ: "$jwt.uid" } } } } } + { node: { group_SOME: { contributors_SOME: { user_SOME: { id_EQ: "$jwt.uid" } } } } } + { node: { group_SOME: { creator_SOME: { id_EQ: "$jwt.uid" } } } } ] } } ] ) { - id: ID! @id @unique + id: ID! @id name: String! - creator: ${User.name}! + creator: [${User.name}!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) @settable(onCreate: true, onUpdate: true) - group: ${Group.name}! @relationship(type: "MEMBER_OF", direction: OUT) + group: [${Group.name}!]! @relationship(type: "MEMBER_OF", direction: OUT) partners: [${Person.name}!]! @relationship( type: "PARTNER_OF" - queryDirection: UNDIRECTED_ONLY + queryDirection: UNDIRECTED direction: OUT properties: "PartnerOf" ) @@ -124,32 +124,32 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { id: ID! email: String! name: String - creator: ${User.name}! @declareRelationship - group: ${Group.name}! @declareRelationship + creator: [${User.name}!]! @declareRelationship + group: [${Group.name}!]! @declareRelationship status: InviteeStatus! - user: ${User.name} @declareRelationship + user: [${User.name}!]! @declareRelationship role: InviteeRole! } type ${Admin.name} implements Invitee @node { - id: ID! @unique @id - group: ${Group.name}! @relationship(type: "ADMIN_OF", direction: OUT) - creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN) + id: ID! @id + group: [${Group.name}!]! @relationship(type: "ADMIN_OF", direction: OUT) + creator: [${User.name}!]! @relationship(type: "CREATOR_OF", direction: IN) email: String! name: String status: InviteeStatus! @default(value: INVITED) - user: ${User.name} @relationship(type: "IS_USER", direction: OUT) + user: [${User.name}!]! @relationship(type: "IS_USER", direction: OUT) role: InviteeRole! @default(value: ADMIN) } type ${Contributor.name} implements Invitee @node { - id: ID! @unique @id - group: ${Group.name}! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) - creator: ${User.name}! @relationship(type: "CREATOR_OF", direction: IN) + id: ID! @id + group: [${Group.name}!]! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) + creator: [${User.name}!]! @relationship(type: "CREATOR_OF", direction: IN) email: String! name: String status: InviteeStatus! @default(value: INVITED) - user: ${User.name} @relationship(type: "IS_USER", direction: OUT) + user: [${User.name}!]! @relationship(type: "IS_USER", direction: OUT) role: InviteeRole! @default(value: CONTRIBUTOR) } diff --git a/packages/graphql/tests/integration/issues/440.int.test.ts b/packages/graphql/tests/integration/issues/440.int.test.ts index 17d78f97d9..2e28e32783 100644 --- a/packages/graphql/tests/integration/issues/440.int.test.ts +++ b/packages/graphql/tests/integration/issues/440.int.test.ts @@ -42,12 +42,12 @@ describe("https://github.com/neo4j/graphql/issues/440", () => { typeDefs = ` type ${Video} @node { - id: ID! @unique + id: ID! categories: [${Category}!]! @relationship(type: "IS_CATEGORIZED_AS", direction: OUT) } type ${Category} @node { - id: ID! @unique + id: ID! videos: [${Video}!]! @relationship(type: "IS_CATEGORIZED_AS", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/4429.int.test.ts b/packages/graphql/tests/integration/issues/4429.int.test.ts index 38ed9c06ef..48b1277996 100644 --- a/packages/graphql/tests/integration/issues/4429.int.test.ts +++ b/packages/graphql/tests/integration/issues/4429.int.test.ts @@ -56,29 +56,29 @@ describe("https://github.com/neo4j/graphql/issues/4429", () => { roles: [String] } type ${User.name} @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant.name}!]! @relationship(type: "ADMIN_IN", direction: OUT) } type ${Tenant.name} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { id: ID! @id - settings: ${Settings.name}! @relationship(type: "VEHICLECARD_OWNER", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VEHICLECARD_OWNER", direction: IN) admins: [${User.name}!]! @relationship(type: "ADMIN_IN", direction: IN) } - type ${Settings.name} @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { + type ${Settings.name} @authorization(validate: [{ where: { node: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) @node { id: ID! @id openingDays: [${OpeningDay.name}!]! @relationship(type: "VALID_GARAGES", direction: OUT) - tenant: ${Tenant.name}! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) + tenant: [${Tenant.name}!]! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) } type ${OpeningDay.name} @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [{ where: { node: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] ) { id: ID! @id - settings: ${Settings.name} @relationship(type: "VALID_GARAGES", direction: IN) + settings: [${Settings.name}!]! @relationship(type: "VALID_GARAGES", direction: IN) open: [${OpeningHoursInterval.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) } @@ -86,11 +86,11 @@ describe("https://github.com/neo4j/graphql/issues/4429", () => { @node @authorization( validate: [ - { where: { node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } + { where: { node: { openingDay_SOME: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } ] ) { name: String - openingDay: ${OpeningDay.name}! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) + openingDay: [${OpeningDay.name}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) updatedBy: String diff --git a/packages/graphql/tests/integration/issues/4450.int.test.ts b/packages/graphql/tests/integration/issues/4450.int.test.ts deleted file mode 100644 index d5e9050d25..0000000000 --- a/packages/graphql/tests/integration/issues/4450.int.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/4450", () => { - const testHelper = new TestHelper(); - - let Actor: UniqueType; - let Scene: UniqueType; - let Location: UniqueType; - - beforeAll(async () => { - Actor = testHelper.createUniqueType("Actor"); - Scene = testHelper.createUniqueType("Scene"); - Location = testHelper.createUniqueType("Location"); - - const typeDefs = /* GraphQL */ ` - type ${Actor} @node { - name: String - scene: [${Scene}!]! @relationship(type: "IN_SCENE", properties: "ActorScene", direction: OUT) - } - - type ${Scene} @node { - number: Int - actors: [${Actor}!]! @relationship(type: "IN_SCENE", properties: "ActorScene", direction: IN) - location: ${Location}! @relationship(type: "AT_LOCATION", direction: OUT) - } - - type ${Location} @node { - city: String - scenes: [${Scene}!]! @relationship(type: "AT_LOCATION", direction: IN) - } - - type ActorScene @relationshipProperties { - cut: Boolean - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - await testHelper.executeCypher( - ` - CREATE (:${Actor} {name: "actor-1"})-[:IN_SCENE {cut: true}]->(:${Scene} {number: 1})-[:AT_LOCATION]->(:${Location} {city: "test"}) - ` - ); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("filtering through a connection to a many-to-1 relationship should work", async () => { - const query = /* GraphQL */ ` - query { - ${Actor.plural}(where: { sceneConnection_SOME: { edge: { cut_EQ: true }, node: { location: { city_EQ: "test" } } } }) { - name - } - } - `; - - const response = await testHelper.executeGraphQL(query); - - expect(response.errors).toBeFalsy(); - expect(response.data).toEqual({ - [Actor.plural]: [ - { - name: "actor-1", - }, - ], - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/4477.int.test.ts b/packages/graphql/tests/integration/issues/4477.int.test.ts index 4a9580a0a3..66fa674361 100644 --- a/packages/graphql/tests/integration/issues/4477.int.test.ts +++ b/packages/graphql/tests/integration/issues/4477.int.test.ts @@ -43,7 +43,7 @@ describe("https://github.com/neo4j/graphql/issues/4477", () => { } type ${Service} @node { - collection: ${Collection} @relationship(type: "HAS_SERVICE", direction: IN) + collection: [${Collection}!]! @relationship(type: "HAS_SERVICE", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/4583.int.test.ts b/packages/graphql/tests/integration/issues/4583.int.test.ts index b228d45feb..1ea2903c8b 100644 --- a/packages/graphql/tests/integration/issues/4583.int.test.ts +++ b/packages/graphql/tests/integration/issues/4583.int.test.ts @@ -52,7 +52,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -147,7 +147,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { actedIn: { connect: { edge: { screenTime: 10 } - where: { node: { title_EQ: "${movieTitle}", typename_IN: [${Movie.name}] } } + where: { node: { title_EQ: "${movieTitle}", typename: [${Movie.name}] } } } } } @@ -198,7 +198,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { connect: { actedIn: { edge: { screenTime: 25 } - where: { node: { title_EQ: "${sameTitle}", typename_IN: [${Movie.name}]} } + where: { node: { title_EQ: "${sameTitle}", typename: [${Movie.name}]} } } } } @@ -261,8 +261,8 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { connect: { edge: { screenTime: 10 } where: { node: { OR: [ - { title_EQ: "${movieTitle}", typename_IN: [${Movie.name}]}, - { AND: [ {typename_IN: [${Series.name}]}, { NOT: { title_EQ: "${sameTitle}"} }] } + { title_EQ: "${movieTitle}", typename: [${Movie.name}]}, + { AND: [ {typename: [${Series.name}]}, { NOT: { title_EQ: "${sameTitle}"} }] } ] } } } } diff --git a/packages/graphql/tests/integration/issues/464.int.test.ts b/packages/graphql/tests/integration/issues/464.int.test.ts index 2506ecbda3..bcfca0b3b1 100644 --- a/packages/graphql/tests/integration/issues/464.int.test.ts +++ b/packages/graphql/tests/integration/issues/464.int.test.ts @@ -70,7 +70,7 @@ describe("https://github.com/neo4j/graphql/issues/464", () => { type ${typeBook.name} @node { id: ID! name: String! - author: ${typeAuthor.name}! @relationship(type: "WROTE", direction: IN) + author: [${typeAuthor.name}!]! @relationship(type: "WROTE", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/4667.int.test.ts b/packages/graphql/tests/integration/issues/4667.int.test.ts deleted file mode 100644 index 8eaf189d87..0000000000 --- a/packages/graphql/tests/integration/issues/4667.int.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/4667", () => { - const testHelper = new TestHelper(); - - let MyThing: UniqueType; - let MyStuff: UniqueType; - - beforeEach(async () => { - MyThing = testHelper.createUniqueType("MyThing"); - MyStuff = testHelper.createUniqueType("MyStuff"); - - await testHelper.executeCypher(` - CREATE (:${MyThing} {id: "A"})-[:THE_STUFF]->(b1:${MyStuff} {id: "C"}) - CREATE (:${MyThing} {id: "B"}) - `); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("when passed null as an argument of a relationship filter should check that a relationship does not exist", async () => { - const typeDefs = /* GraphQL */ ` - type ${MyThing} @node { - id: ID! @id - stuff: ${MyStuff} @relationship(type: "THE_STUFF", direction: OUT) - } - - type ${MyStuff} @node { - id: ID! @id - thing: ${MyThing} @relationship(type: "THE_STUFF", direction: IN) - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - ${MyThing.plural}(where: { stuff: null }) { - id - stuff { - id - } - } - - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [MyThing.plural]: expect.toIncludeSameMembers([expect.objectContaining({ id: "B" })]), - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/4704.int.test.ts b/packages/graphql/tests/integration/issues/4704.int.test.ts index 069b5f2ac3..da94afa4d5 100644 --- a/packages/graphql/tests/integration/issues/4704.int.test.ts +++ b/packages/graphql/tests/integration/issues/4704.int.test.ts @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { const typeDefs = gql` type ${Episode} @node { runtime: Int! - series: ${Series}! @relationship(type: "HAS_EPISODE", direction: IN) + series: [${Series}!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { diff --git a/packages/graphql/tests/integration/issues/487.int.test.ts b/packages/graphql/tests/integration/issues/487.int.test.ts index fac40b52e8..f616ab5622 100644 --- a/packages/graphql/tests/integration/issues/487.int.test.ts +++ b/packages/graphql/tests/integration/issues/487.int.test.ts @@ -47,12 +47,12 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { type ${typeBook.name} @node { id: ID! - author: ${typeAuthor.name}! @relationship(type: "WROTE", direction: IN) + author: [${typeAuthor.name}!]! @relationship(type: "WROTE", direction: IN) } type ${typeMovie.name} @node { id: ID! - director: ${typeDirector.name}! @relationship(type: "DIRECTED", direction: IN) + director: [${typeDirector.name}!]! @relationship(type: "DIRECTED", direction: IN) } union Thing = ${typeBook.name} | ${typeMovie.name} @@ -130,17 +130,21 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { expect(movie).toEqual({ id: movieId, - director: { - id: directorId, - }, + director: [ + { + id: directorId, + }, + ], __typename: typeMovie.name, }); expect(book).toEqual({ id: bookId, - author: { - id: authorId, - }, + author: [ + { + id: authorId, + }, + ], __typename: typeBook.name, }); }); @@ -162,12 +166,12 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { type ${typeBook.name} implements Thing @node { id: ID! - author: ${typeAuthor.name}! @relationship(type: "WROTE", direction: IN) + author: [${typeAuthor.name}!]! @relationship(type: "WROTE", direction: IN) } type ${typeMovie.name} implements Thing @node { id: ID! - director: ${typeDirector.name}! @relationship(type: "DIRECTED", direction: IN) + director: [${typeDirector.name}!]! @relationship(type: "DIRECTED", direction: IN) } interface Thing { @@ -247,17 +251,21 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { expect(movie).toEqual({ id: movieId, - director: { - id: directorId, - }, + director: [ + { + id: directorId, + }, + ], __typename: typeMovie.name, }); expect(book).toEqual({ id: bookId, - author: { - id: authorId, - }, + author: [ + { + id: authorId, + }, + ], __typename: typeBook.name, }); }); diff --git a/packages/graphql/tests/integration/issues/488.int.test.ts b/packages/graphql/tests/integration/issues/488.int.test.ts index 592c6f0db0..3a39442e96 100644 --- a/packages/graphql/tests/integration/issues/488.int.test.ts +++ b/packages/graphql/tests/integration/issues/488.int.test.ts @@ -45,17 +45,17 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { union Keyword = ${testEmoji.name} | ${testHashtag.name} | ${testText.name} type ${testEmoji.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } type ${testHashtag.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } type ${testText.name} @node { - id: ID! @id @unique + id: ID! @id type: String! } `; diff --git a/packages/graphql/tests/integration/issues/5023.int.test.ts b/packages/graphql/tests/integration/issues/5023.int.test.ts index ad53291dbd..6216a6f8bc 100644 --- a/packages/graphql/tests/integration/issues/5023.int.test.ts +++ b/packages/graphql/tests/integration/issues/5023.int.test.ts @@ -42,28 +42,28 @@ describe("https://github.com/neo4j/graphql/issues/5013", () => { id: String } type ${User} @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } type ${Tenant} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { id: ID! @id admins: [${User}!]! @relationship(type: "ADMIN_IN", direction: IN, aggregate: false) - settings: ${Settings}! @relationship(type: "HAS_SETTINGS", direction: OUT, aggregate: false) + settings: [${Settings}!]! @relationship(type: "HAS_SETTINGS", direction: OUT, aggregate: false) } type ${Settings} @node - @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { - tenant: ${Tenant}! @relationship(type: "HAS_SETTINGS", direction: IN, aggregate: false) + @authorization(validate: [{ where: { node: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { + tenant: [${Tenant}!]! @relationship(type: "HAS_SETTINGS", direction: IN, aggregate: false) extendedOpeningHours: [${OpeningDay}!]! @relationship(type: "HAS_OPENING_HOURS", direction: OUT, aggregate: false) } type ${OpeningDay} @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [{ where: { node: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] ) { - settings: ${Settings}! @relationship(type: "HAS_OPENING_HOURS", direction: IN, aggregate: false) + settings: [${Settings}!]! @relationship(type: "HAS_OPENING_HOURS", direction: IN, aggregate: false) date: Date open: [${OpeningHoursInterval}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT, aggregate: false) @@ -72,10 +72,10 @@ describe("https://github.com/neo4j/graphql/issues/5013", () => { type ${OpeningHoursInterval} @node @authorization( validate: [ - { where: { node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } + { where: { node: { openingDay_SOME: { settings_SOME: { tenant_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } } } ] ) { - openingDay: ${OpeningDay} @relationship(type: "HAS_OPEN_INTERVALS", direction: IN, aggregate: false) + openingDay: [${OpeningDay}!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN, aggregate: false) name: String } diff --git a/packages/graphql/tests/integration/issues/5030.int.test.ts b/packages/graphql/tests/integration/issues/5030.int.test.ts index a7da371231..63e2ade95c 100644 --- a/packages/graphql/tests/integration/issues/5030.int.test.ts +++ b/packages/graphql/tests/integration/issues/5030.int.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/5030", () => { Movie = testHelper.createUniqueType("Movie"); const typeDefs = /* GraphQL */ ` - type ${Movie} @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) @node { + type ${Movie} @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) @node { title: String released: Int } diff --git a/packages/graphql/tests/integration/issues/505.int.test.ts b/packages/graphql/tests/integration/issues/505.int.test.ts index b38b1a387f..b83002c229 100644 --- a/packages/graphql/tests/integration/issues/505.int.test.ts +++ b/packages/graphql/tests/integration/issues/505.int.test.ts @@ -71,12 +71,12 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { where: { node: { OR: [ - { owner: { authId_EQ: "$jwt.sub" } } + { owner_SOME: { authId_EQ: "$jwt.sub" } } { AND: [ { shared_EQ: true } { - workspace: { + workspace_SOME: { OR: [ { members_SOME: { authId_EQ: "$jwt.sub" } } { admins_SOME: { authId_EQ: "$jwt.sub" } } @@ -95,9 +95,9 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { shared: Boolean! @default(value: false) - owner: ${userType}! @relationship(type: "CREATED_PAGE", direction: IN) + owner: [${userType}!]! @relationship(type: "CREATED_PAGE", direction: IN) - workspace: ${workspaceType}! @relationship(type: "HAS_PAGE", direction: IN) + workspace: [${workspaceType}!]! @relationship(type: "HAS_PAGE", direction: IN) } `; }); @@ -141,7 +141,7 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { `; const pagesQuery = ` query Pages($workspaceId: ID!) { - ${pageType.plural}(where: { workspace: { id_EQ: $workspaceId } }) { + ${pageType.plural}(where: { workspace_SOME: { id_EQ: $workspaceId } }) { id } } diff --git a/packages/graphql/tests/integration/issues/5066.int.test.ts b/packages/graphql/tests/integration/issues/5066.int.test.ts deleted file mode 100644 index fb38f9d3ac..0000000000 --- a/packages/graphql/tests/integration/issues/5066.int.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createBearerToken } from "../../utils/create-bearer-token"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/5066", () => { - let User: UniqueType; - let AdminGroup: UniqueType; - let UserBlockedUser: UniqueType; - let Party: UniqueType; - - const secret = "secret"; - const testHelper = new TestHelper(); - - beforeEach(async () => { - User = testHelper.createUniqueType("User"); - AdminGroup = testHelper.createUniqueType("AdminGroup"); - UserBlockedUser = testHelper.createUniqueType("UserBlockedUser"); - Party = testHelper.createUniqueType("Party"); - - const typeDefs = /* GraphQL */ ` - type ${AdminGroup} @node(labels: ["${AdminGroup}"]) @mutation(operations: []) @authorization( - filter: [ - { where: { node: { createdBy: { id_EQ: "$jwt.sub" } } } }, - ] - ) { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - createdBy: ${User}! @relationship(type: "CREATED_ADMIN_GROUP", direction: IN) @settable(onCreate: true, onUpdate: false) - } - - type ${User} @node(labels: ["${User}"]) @mutation(operations: []) @authorization( - filter: [ - { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, - ] - ) { - id: ID! @unique @settable(onCreate: true, onUpdate: false) - createdAt: DateTime! @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - username: String! @unique - blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) - createdAdminGroups: [${AdminGroup}!]! @relationship(type: "CREATED_ADMIN_GROUP", direction: OUT) - } - - type ${UserBlockedUser} @node(labels: ["${UserBlockedUser}"]) @query(read: false, aggregate: false) @mutation(operations: []) @authorization( - filter: [ - { where: { node: { from: { id_EQ: "$jwt.sub" } } } } - ] - ) { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) - to: ${User}! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) - } - - union PartyCreator = ${User} | ${AdminGroup} - - type ${Party} @node(labels: ["${Party}"]) @mutation(operations: []) @authorization( - filter: [ - { where: { node: { createdByConnection: { ${User}: { node: { id_EQ: "$jwt.sub" } } } } } }, - { where: { node: { createdByConnection: { ${AdminGroup}: { node: { createdBy: { id_EQ: "$jwt.sub" } } } } } } }, - ] - ){ - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - createdBy: PartyCreator! @relationship(type: "CREATED_PARTY", direction: IN) @settable(onCreate: true, onUpdate: false) - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should return filtered results according to authorization rule", async () => { - const query = ` - query Parties { - ${Party.plural} { - id - createdBy { - ... on ${User} { - username - } - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (p:${Party} { id: "1" })<-[:CREATED_PARTY]-(u:${User} { id: "1", username: "arthur" }); - `); - - const token = createBearerToken(secret, { sub: "1" }); - const result = await testHelper.executeGraphQLWithToken(query, token); - expect(result.errors).toBeUndefined(); - expect(result.data as any).toEqual({ - [Party.plural]: [{ id: "1", createdBy: { username: "arthur" } }], - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/5080.int.test.ts b/packages/graphql/tests/integration/issues/5080.int.test.ts index 2ae64b77dc..422126c52c 100644 --- a/packages/graphql/tests/integration/issues/5080.int.test.ts +++ b/packages/graphql/tests/integration/issues/5080.int.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { id: String } type ${User} @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } type ${Tenant} @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { @@ -57,9 +57,9 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { type ${Car} @node @mutation(operations: [UPDATE]) - @authorization(validate: [{ where: { node: { owner: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { + @authorization(validate: [{ where: { node: { owner_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { id: ID! @id - owner: ${Tenant}! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) + owner: [${Tenant}!]! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) name: String! createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -67,9 +67,9 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { type ${DeletedCar} @node @mutation(operations: [UPDATE]) - @authorization(validate: [{ where: { node: { owner: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { + @authorization(validate: [{ where: { node: { owner_SOME: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { id: ID! @id - owner: ${Tenant}! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) + owner: [${Tenant}!]! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) name: String! reason: String! createdAt: DateTime! @timestamp(operations: [CREATE]) diff --git a/packages/graphql/tests/integration/issues/5143.int.test.ts b/packages/graphql/tests/integration/issues/5143.int.test.ts index 8f7a7cd4c8..0d93df9c4d 100644 --- a/packages/graphql/tests/integration/issues/5143.int.test.ts +++ b/packages/graphql/tests/integration/issues/5143.int.test.ts @@ -39,9 +39,9 @@ describe("https://github.com/neo4j/graphql/issues/5143", () => { type ${Video} @node { id: ID! @id - publisher: ${User}! @relationship(type: "PUBLISHER", direction: IN) + publisher: [${User}!]! @relationship(type: "PUBLISHER", direction: IN) } - extend type ${Video} @authorization(filter: [{ where: { node: { publisher: { id_EQ: "$jwt.sub" } } } }]) + extend type ${Video} @authorization(filter: [{ where: { node: { publisher_SOME: { id_EQ: "$jwt.sub" } } } }]) type Query { getAllVids: [${Video}]! diff --git a/packages/graphql/tests/integration/issues/5270.int.test.ts b/packages/graphql/tests/integration/issues/5270.int.test.ts index 38f4b20c43..0a89f188f4 100644 --- a/packages/graphql/tests/integration/issues/5270.int.test.ts +++ b/packages/graphql/tests/integration/issues/5270.int.test.ts @@ -35,21 +35,21 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { const typeDefs = /* GraphQL */ ` type ${User} @node(labels: ["${User}"]) @authorization( filter: [ - { where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }, + { where: { node: { NOT: { blockedUsers_SOME: { to_SOME: { id_EQ: "$jwt.sub" } } } } } }, ] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [${UserBlockedUser}!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } type ${UserBlockedUser} @node(labels: ["${UserBlockedUser}"]) @authorization( filter: [ - { where: { node: { from: { id_EQ: "$jwt.sub" } } } } + { where: { node: { from_SOME: { id_EQ: "$jwt.sub" } } } } ] ) { - id: ID! @id @unique - from: ${User}! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) - to: ${User}! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) + id: ID! @id + from: [${User}!]! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) + to: [${User}!]! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) } type Query { diff --git a/packages/graphql/tests/integration/issues/5378.int.test.ts b/packages/graphql/tests/integration/issues/5378.int.test.ts index 14a275842c..c6dbbf04a9 100644 --- a/packages/graphql/tests/integration/issues/5378.int.test.ts +++ b/packages/graphql/tests/integration/issues/5378.int.test.ts @@ -55,8 +55,8 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { const typeDefs = /* GraphQL */ ` type ${Space} @node - @fulltext(indexes: [{ indexName: "fulltext_index_space_name_number", fields: ["Name", "Number"] }]) { - Id: ID! @id @unique + @fulltext(indexes: [{ indexName: "fulltext_index_space_name_number", queryName: "spacesByNameAndNumber", fields: ["Name", "Number"] }]) { + Id: ID! @id Number: String Name: String! } @@ -96,11 +96,7 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { const query = /* GraphQL */ ` query SpacesSearchConnection { - ${Space.operations.connection}(fulltext: { - fulltext_index_space_name_number: { - phrase: "Bedroom" - } - }) { + spacesByNameAndNumber(phrase: "Bedroom") { totalCount edges { node { @@ -109,13 +105,6 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { } } } - ${Space.operations.aggregate}(fulltext: { - fulltext_index_space_name_number: { - phrase: "Bedroom" - } - }) { - count - } } `; @@ -128,7 +117,7 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { const gqlResult = await testHelper.executeGraphQL(query); expect(gqlResult.errors).toBeFalsy(); expect(gqlResult.data).toEqual({ - [Space.operations.connection]: { + spacesByNameAndNumber: { totalCount: 2, edges: expect.toIncludeSameMembers([ { @@ -145,9 +134,6 @@ describe("https://github.com/neo4j/graphql/issues/5378", () => { }, ]), }, - [Space.operations.aggregate]: { - count: 2, - }, }); }); }); diff --git a/packages/graphql/tests/integration/issues/549.int.test.ts b/packages/graphql/tests/integration/issues/549.int.test.ts deleted file mode 100644 index af64c225b9..0000000000 --- a/packages/graphql/tests/integration/issues/549.int.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { gql } from "graphql-tag"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/549", () => { - const testHelper = new TestHelper(); - - beforeAll(() => {}); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should throw when creating a node without a mandatory relationship", async () => { - const testPerson = testHelper.createUniqueType("Person"); - const testMovie = testHelper.createUniqueType("Movie"); - - const typeDefs = gql` - type ${testPerson.name} @node { - name: String! - born: Int! - actedInMovies: [${testMovie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - directedMovies: [${testMovie.name}!]! @relationship(type: "DIRECTED", direction: OUT) - } - - type ${testMovie.name} @node { - title: String! - released: Int! - actors: [${testPerson.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - director: ${testPerson.name}! @relationship(type: "DIRECTED", direction: IN) - } - - type ActedIn @relationshipProperties { - roles: [String!] - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const query = ` - mutation { - ${testMovie.operations.create}(input: [{title: "Test", released: 2022}]) { - ${testMovie.plural} { - title - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${testMovie.name}.director required exactly once`); - }); -}); diff --git a/packages/graphql/tests/integration/issues/5497.int.test.ts b/packages/graphql/tests/integration/issues/5497.int.test.ts index c689aa35c6..b98cd8458d 100644 --- a/packages/graphql/tests/integration/issues/5497.int.test.ts +++ b/packages/graphql/tests/integration/issues/5497.int.test.ts @@ -51,21 +51,21 @@ describe("https://github.com/neo4j/graphql/issues/5467", () => { cabinets: [${Cabinet}!]! @relationship(type: "HAS_CABINET", direction: OUT) } - type ${Cabinet} @authorization(filter: [{ where: { node: { user: { id_EQ: "$jwt.sub" } } } }]) @node { + type ${Cabinet} @authorization(filter: [{ where: { node: { user_SINGLE: { id_EQ: "$jwt.sub" } } } }]) @node { id: ID! @id categories: [${Category}!]! @relationship(type: "HAS_CATEGORY", direction: OUT) - user: ${User}! @relationship(type: "HAS_CABINET", direction: IN) + user: [${User}!]! @relationship(type: "HAS_CABINET", direction: IN) } - type ${Category} @authorization(filter: [{ where: { node: { cabinet: { user: { id_EQ: "$jwt.sub" } } } } }]) @node { + type ${Category} @authorization(filter: [{ where: { node: { cabinet_SINGLE: { user_SINGLE: { id_EQ: "$jwt.sub" } } } } }]) @node { id: ID! @id files: [${File}!]! @relationship(type: "HAS_FILE", direction: OUT) - cabinet: ${Cabinet}! @relationship(type: "HAS_CATEGORY", direction: IN) + cabinet: [${Cabinet}!]! @relationship(type: "HAS_CATEGORY", direction: IN) } type ${File} @node { - id: ID! @unique - category: ${Category} @relationship(type: "HAS_FILE", direction: IN) + id: ID! + category: [${Category}!]! @relationship(type: "HAS_FILE", direction: IN) } `; await testHelper.initNeo4jGraphQL({ diff --git a/packages/graphql/tests/integration/issues/5515.int.test.ts b/packages/graphql/tests/integration/issues/5515.int.test.ts index 8e30f8ef32..8d154356a9 100644 --- a/packages/graphql/tests/integration/issues/5515.int.test.ts +++ b/packages/graphql/tests/integration/issues/5515.int.test.ts @@ -54,21 +54,21 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { cabinets: [${Cabinet}!]! @relationship(type: "HAS_CABINET", direction: OUT) } - type ${Cabinet} @authorization(filter: [{ where: { node: { user: { id_EQ: "$jwt.sub" } } } }]) @node { + type ${Cabinet} @authorization(filter: [{ where: { node: { user_SINGLE: { id_EQ: "$jwt.sub" } } } }]) @node { id: ID! @id categories: [${Category}!]! @relationship(type: "HAS_CATEGORY", direction: OUT) - user: ${User}! @relationship(type: "HAS_CABINET", direction: IN) + user: [${User}!]! @relationship(type: "HAS_CABINET", direction: IN) } - type ${Category} @authorization(filter: [{ where: { node: { cabinet: { user: { id_EQ: "$jwt.sub" } } } } }]) @node { + type ${Category} @authorization(filter: [{ where: { node: { cabinet_SINGLE: { user_SINGLE: { id_EQ: "$jwt.sub" } } } } }]) @node { id: ID! @id files: [${File}!]! @relationship(type: "HAS_FILE", direction: OUT) - cabinet: ${Cabinet}! @relationship(type: "HAS_CATEGORY", direction: IN) + cabinet: [${Cabinet}!]! @relationship(type: "HAS_CATEGORY", direction: IN) } type ${File} @node { - id: ID! @unique - category: ${Category} @relationship(type: "HAS_FILE", direction: IN) + id: ID! + category: [${Category}!]! @relationship(type: "HAS_FILE", direction: IN) } `; await testHelper.initNeo4jGraphQL({ diff --git a/packages/graphql/tests/integration/issues/556.int.test.ts b/packages/graphql/tests/integration/issues/556.int.test.ts index fd2fb099c7..b837c1be43 100644 --- a/packages/graphql/tests/integration/issues/556.int.test.ts +++ b/packages/graphql/tests/integration/issues/556.int.test.ts @@ -37,7 +37,7 @@ describe("https://github.com/neo4j/graphql/issues/556 - Input Object type Articl } type ${Thing} @node { - id: ID! @id @unique + id: ID! @id } `; }); diff --git a/packages/graphql/tests/integration/issues/5599.int.test.ts b/packages/graphql/tests/integration/issues/5599.int.test.ts index 5187a585c2..6d512bc898 100644 --- a/packages/graphql/tests/integration/issues/5599.int.test.ts +++ b/packages/graphql/tests/integration/issues/5599.int.test.ts @@ -72,7 +72,7 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { const query = /* GraphQL */ ` mutation UpdateMovies { ${Movie.operations.update}( - update: { actors: { ${LeadActor}: [{ delete: [{ where: { node: { name: "Actor1" } } }] }] } } + update: { actors: { ${LeadActor}: [{ delete: [{ where: { node: { name_EQ: "Actor1" } } }] }] } } ) { ${Movie.plural} { title @@ -110,7 +110,7 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { const query = /* GraphQL */ ` mutation UpdateMovies { ${Movie.operations.update}( - update: { actors: { ${LeadActor}: [{ delete: [{ where: { node: { name: "Actor1" } } }] }], ${Extra}: [{ delete: [{ where: { node: { name: "Actor2" } } }] }] } } + update: { actors: { ${LeadActor}: [{ delete: [{ where: { node: { name_EQ: "Actor1" } } }] }], ${Extra}: [{ delete: [{ where: { node: { name_EQ: "Actor2" } } }] }] } } ) { ${Movie.plural} { title diff --git a/packages/graphql/tests/integration/issues/5635.int.test.ts b/packages/graphql/tests/integration/issues/5635.int.test.ts deleted file mode 100644 index 451c4bf3e8..0000000000 --- a/packages/graphql/tests/integration/issues/5635.int.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/5635", () => { - let Owner: UniqueType; - let MyNode: UniqueType; - - const testHelper = new TestHelper(); - - beforeEach(async () => { - Owner = testHelper.createUniqueType("Owner"); - MyNode = testHelper.createUniqueType("MyNode"); - - const typeDefs = /* GraphQL */ ` - type ${Owner} { - id: ID! @unique @id - owns: [${MyNode}!]! @relationship(type: "OWNS", direction: OUT) - } - - type ${MyNode} - @authorization( - validate: [ - { - operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] - where: { node: { owner: { id: "$jwt.sub" } } } - when: [AFTER] - } - ] - ) { - id: ID! @unique @id - name: String! - owner: ${Owner}! @relationship(type: "OWNS", direction: IN) - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("validation should applied correctly without causing cypher errors", async () => { - await testHelper.executeCypher(` - CREATE (c:${MyNode.name} {id: 'abc'})<-[:OWNS]-(o:${Owner.name} {id: 'abc'}) - `); - - const mutation = /* GraphQL */ ` - mutation { - ${MyNode.operations.create}(input: [{ - name: "Test", - owner: { - connectOrCreate: { - onCreate: { - node: { } - }, - where: { node: { id: "abc" } } - } - } - }]) { - ${MyNode.plural} { - id - name - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation, { - contextValue: { - token: testHelper.createBearerToken("secret", { sub: "abc" }), - }, - }); - expect(result.errors).toBeUndefined(); - expect(result.data as any).toEqual({ - [MyNode.operations.create]: { - [MyNode.plural]: [ - { - id: expect.any(String), - name: "Test", - }, - ], - }, - }); - }); -}); diff --git a/packages/graphql/tests/integration/issues/5681.int.test.ts b/packages/graphql/tests/integration/issues/5681.int.test.ts index b4bd1b6700..73b63b0940 100644 --- a/packages/graphql/tests/integration/issues/5681.int.test.ts +++ b/packages/graphql/tests/integration/issues/5681.int.test.ts @@ -51,9 +51,10 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { } type ${User} + @node @authorization( validate: [ - { where: { node: { userId: "$jwt.sub" } } } + { where: { node: { userId_EQ: "$jwt.sub" } } } { where: { jwt: { roles_INCLUDES: "overlord" } } } ] ) @@ -61,7 +62,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { operations: [UPDATE, DELETE, CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, SUBSCRIBE] jwt: { roles_INCLUDES: "overlord" } ) { - userId: String! @unique + userId: String! adminAccess: [${Tenant}!]! @relationship(type: "ADMIN_IN", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -69,6 +70,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { } type ${Tenant} + @node @authentication( operations: [UPDATE, DELETE, CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, SUBSCRIBE] jwt: { roles_INCLUDES: "overlord" } @@ -83,24 +85,26 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { } type ${Garage} + @node @authentication( operations: [UPDATE, DELETE, CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, SUBSCRIBE] jwt: { roles_INCLUDES: "overlord" } ) { id: ID! @id - tenant: ${Tenant}! @relationship(type: "TENANT_HAS_GARAGE", direction: OUT) + tenant: [${Tenant}!]! @relationship(type: "TENANT_HAS_GARAGE", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } type ${VehicleCard} + @node @authentication( operations: [UPDATE, DELETE, CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, SUBSCRIBE] jwt: { roles_INCLUDES: "overlord" } ) { id: ID! @id - tenant: ${Tenant}! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) # <--- this line + tenant: [${Tenant}!]! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) # <--- this line garages: [${Garage}!]! @relationship(type: "VALID_GARAGES", direction: OUT) createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -129,7 +133,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { test("should not throw invalid argument error", async () => { const myUserId = "userId"; - const ADD_TENANT = /*GraphQL*/ ` + const ADD_TENANT = /* GraphQL */ ` mutation addTenant($input: [${Tenant}CreateInput!]!) { ${Tenant.operations.create}(input: $input) { ${Tenant.plural} { @@ -167,7 +171,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { }, }); - const ADD_GARAGES = /*GraphQL*/ ` + const ADD_GARAGES = /* GraphQL */ ` mutation addGarages($input: [${Garage}CreateInput!]!) { ${Garage.operations.create}(input: $input) { ${Garage.plural} { @@ -181,7 +185,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { connect: { where: { node: { - id: tenantId, + id_EQ: tenantId, }, }, }, @@ -205,7 +209,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { }, }); - const ADD_VEHICLE_CARD = /*GraphQL*/ ` + const ADD_VEHICLE_CARD = /* GraphQL */ ` mutation addVehicleCard($input: [${VehicleCard}CreateInput!]!) { ${VehicleCard.operations.create}(input: $input) { ${VehicleCard.plural} { @@ -219,7 +223,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { connect: { where: { node: { - id: tenantId, + id_EQ: tenantId, }, }, }, @@ -228,7 +232,7 @@ describe("https://github.com/neo4j/graphql/issues/5635", () => { connect: { where: { node: { - id: garageId, + id_EQ: garageId, }, }, }, diff --git a/packages/graphql/tests/integration/issues/579.int.test.ts b/packages/graphql/tests/integration/issues/579.int.test.ts index 3e4b8609ec..4d183860dc 100644 --- a/packages/graphql/tests/integration/issues/579.int.test.ts +++ b/packages/graphql/tests/integration/issues/579.int.test.ts @@ -33,7 +33,7 @@ describe("https://github.com/neo4j/graphql/pull/579", () => { typeDefs = ` type ${Product} @node { id: ID - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT, properties: "OfColorProperties") + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT, properties: "OfColorProperties") } type OfColorProperties @relationshipProperties { diff --git a/packages/graphql/tests/integration/issues/date-in-edge.int.test.ts b/packages/graphql/tests/integration/issues/587.int.test.ts similarity index 95% rename from packages/graphql/tests/integration/issues/date-in-edge.int.test.ts rename to packages/graphql/tests/integration/issues/587.int.test.ts index d8744a3ba8..cce28a3fbf 100644 --- a/packages/graphql/tests/integration/issues/date-in-edge.int.test.ts +++ b/packages/graphql/tests/integration/issues/587.int.test.ts @@ -21,7 +21,7 @@ import { generate } from "randomstring"; import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; -describe("587: Dates in edges can cause wrongly generated cypher", () => { +describe("https://github.com/neo4j/graphql/pull/587", () => { const testHelper = new TestHelper(); let typeDefs: string; let Genre: UniqueType; @@ -53,7 +53,7 @@ describe("587: Dates in edges can cause wrongly generated cypher", () => { type ${Actor} @node { name: String! birthday: DateTime! - movie: ${Movie}! @relationship(type: "ACTOR", direction: IN) + movie: [${Movie}!]! @relationship(type: "ACTOR", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/5887.int.test.ts b/packages/graphql/tests/integration/issues/5887.int.test.ts index b9b6e6e407..0a863e0d4f 100644 --- a/packages/graphql/tests/integration/issues/5887.int.test.ts +++ b/packages/graphql/tests/integration/issues/5887.int.test.ts @@ -20,119 +20,6 @@ import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; -describe("https://github.com/neo4j/graphql/issues/5887", () => { - let Base: UniqueType; - let A: UniqueType; - let B: UniqueType; - let Test: UniqueType; - - const testHelper = new TestHelper(); - - beforeEach(async () => { - Base = testHelper.createUniqueType("Base"); - A = testHelper.createUniqueType("A"); - B = testHelper.createUniqueType("B"); - Test = testHelper.createUniqueType("Test"); - - const typeDefs = /* GraphQL */ ` - interface ${Base} { - id: ID! - } - - type ${A} implements ${Base} @node { - id: ID! - } - - type ${B} implements ${Base} @node { - id: ID! - } - - type ${Test} { - base: ${Base}! @relationship(type: "HAS", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - await testHelper.executeCypher(` - CREATE (:${Test})-[:HAS]->(:${A} {id: "1"}) - CREATE (:${Test})-[:HAS]->(:${B} {id: "2"}) - `); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("should return relationship when first interface matches", async () => { - const query = /* GraphQL */ ` - query { - ${Test.plural}(where: { base: { id: "1" } }) { - base { - id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [Test.plural]: [ - { - base: { - id: "1", - }, - }, - ], - }); - }); - - test("should return relationship when second interface matches", async () => { - const query = /* GraphQL */ ` - query { - ${Test.plural}(where: { base: { id: "2" } }) { - base { - id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [Test.plural]: [ - { - base: { - id: "2", - }, - }, - ], - }); - }); - - test("should not return relationship when no interface match", async () => { - const query = /* GraphQL */ ` - query { - ${Test.plural}(where: { base: { id: "x" } }) { - base { - id - } - } - } - `; - - const result = await testHelper.executeGraphQL(query); - expect(result.errors).toBeUndefined(); - expect(result.data).toEqual({ - [Test.plural]: [], - }); - }); -}); - describe("https://github.com/neo4j/graphql/issues/5887 list relationship", () => { let House: UniqueType; let Animal: UniqueType; @@ -148,7 +35,7 @@ describe("https://github.com/neo4j/graphql/issues/5887 list relationship", () => Dog = testHelper.createUniqueType("Dog"); const typeDefs = /* GraphQL */ ` - type ${House} { + type ${House} @node { address: String! animals: [${Animal}!]! @relationship(type: "LIVES_IN", direction: IN) } diff --git a/packages/graphql/tests/integration/issues/619.int.test.ts b/packages/graphql/tests/integration/issues/619.int.test.ts deleted file mode 100644 index c3e2bbaf22..0000000000 --- a/packages/graphql/tests/integration/issues/619.int.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/619", () => { - const testHelper = new TestHelper(); - let typeDefs: string; - let FooIsARandomName: UniqueType; - let BarIsACoolName: UniqueType; - - beforeAll(async () => { - FooIsARandomName = testHelper.createUniqueType("FooIsARandomName"); - BarIsACoolName = testHelper.createUniqueType("BarIsACoolName"); - typeDefs = ` - type ${FooIsARandomName} @node { - id: ID @unique - Name: String - Age: Int - DrinksAt: ${BarIsACoolName} @relationship(type: "DRINKS_AT", direction: OUT) - } - - type ${BarIsACoolName} @node { - id: ID @unique - Adress: String - Customers: [${FooIsARandomName}!]! @relationship(type: "DRINKS_AT", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should not throw 'input.map is not a function' error on one to many mutations", async () => { - const mutation = /* GraphQL */ ` - mutation { - ${FooIsARandomName.operations.create}( - input: { - DrinksAt: { - connectOrCreate: { - where: { node: { id_EQ: "b50bd49b-9295-4749-9c0e-91d1e16df0b5" } } - onCreate: { node: { Adress: "Some Street" } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const gqlResult: any = await testHelper.executeGraphQL(mutation); - - expect(gqlResult.errors).toBeUndefined(); - }); -}); diff --git a/packages/graphql/tests/integration/issues/832.int.test.ts b/packages/graphql/tests/integration/issues/832.int.test.ts index 9eae7ac527..5ba02bdad3 100644 --- a/packages/graphql/tests/integration/issues/832.int.test.ts +++ b/packages/graphql/tests/integration/issues/832.int.test.ts @@ -38,17 +38,17 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { } type ${Person} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Place} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Interaction} @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/847.int.test.ts b/packages/graphql/tests/integration/issues/847.int.test.ts index 06e048695b..4eecd661ce 100644 --- a/packages/graphql/tests/integration/issues/847.int.test.ts +++ b/packages/graphql/tests/integration/issues/847.int.test.ts @@ -38,17 +38,17 @@ describe("https://github.com/neo4j/graphql/issues/847", () => { } type ${personType.name} implements Entity @node { - id : String! @unique + id : String! name : String! } type ${placeType.name} implements Entity @node { - id: String! @unique + id: String! location: Point! } type ${interactionType.name} @node { - id : ID! @id @unique + id : ID! @id kind : String! subjects : [Entity!]! @relationship(type: "ACTED_IN", direction: IN ) objects : [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/issues/894.int.test.ts b/packages/graphql/tests/integration/issues/894.int.test.ts index 9655c0db3d..648ef0de24 100644 --- a/packages/graphql/tests/integration/issues/894.int.test.ts +++ b/packages/graphql/tests/integration/issues/894.int.test.ts @@ -31,13 +31,13 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { const typeDefs = ` type ${testUser.name} @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! - activeOrganization: ${testOrganization.name} @relationship(type: "ACTIVELY_MANAGING", direction: OUT) + activeOrganization: [${testOrganization.name}!]! @relationship(type: "ACTIVELY_MANAGING", direction: OUT) } type ${testOrganization.name} @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! } `; diff --git a/packages/graphql/tests/integration/issues/923.int.test.ts b/packages/graphql/tests/integration/issues/923.int.test.ts deleted file mode 100644 index c6cbb17256..0000000000 --- a/packages/graphql/tests/integration/issues/923.int.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { gql } from "graphql-tag"; -import type { Integer } from "neo4j-driver"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/923", () => { - const testHelper = new TestHelper(); - - let testBlogpost: UniqueType; - let testCategory: UniqueType; - - beforeAll(async () => { - testBlogpost = testHelper.createUniqueType("BlogPost"); - testCategory = testHelper.createUniqueType("Category"); - // driver = await neo4j.getDriver(); - - const typeDefs = gql` - type ${testBlogpost.name} @fulltext(indexes: [{ name: "BlogTitle", fields: ["title"] }]) @node { - title: String! - slug: String! @unique - } - type ${testCategory.name} @node { - name: String! @unique - blogs: [${testBlogpost.name}!]! @relationship(type: "IN_CATEGORY", direction: IN) - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should query nested connection", async () => { - const query = /* GraphQL */ ` - mutation { - ${testCategory.operations.create}( - input: [ - { - blogs: { - connectOrCreate: [ - { - where: { node: { slug_EQ: "dsa" } } - onCreate: { node: { title: "mytitle", slug: "myslug" } } - } - ] - } - name: "myname" - } - ] - ) { - info { - nodesCreated - } - } - } - `; - - const result = await testHelper.executeGraphQL(query, { - contextValue: { - jwt: { - sub: "test", - }, - }, - }); - expect(result.errors).toBeUndefined(); - - const blogPostCount = await testHelper.executeCypher(` - MATCH (m:${testBlogpost.name} { slug: "myslug" }) - RETURN COUNT(m) as count - `); - expect((blogPostCount.records[0]?.toObject().count as Integer).toNumber()).toBe(1); - }); -}); diff --git a/packages/graphql/tests/integration/issues/976.int.test.ts b/packages/graphql/tests/integration/issues/976.int.test.ts deleted file mode 100644 index f64d7cba04..0000000000 --- a/packages/graphql/tests/integration/issues/976.int.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Integer } from "neo4j-driver"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("https://github.com/neo4j/graphql/issues/976", () => { - let testBibliographicReference: UniqueType; - let testConcept: UniqueType; - const testHelper = new TestHelper(); - - beforeAll(async () => { - testBibliographicReference = testHelper.createUniqueType("BibliographicReference"); - testConcept = testHelper.createUniqueType("Concept"); - - const typeDefs = ` - type ${testBibliographicReference.name} @node(labels: ["${testBibliographicReference.name}", "Resource"]){ - iri: ID! @unique @alias(property: "uri") - prefLabel: [String!] - isInPublication: [${testConcept.name}!]! @relationship(type: "isInPublication", direction: OUT) - } - - type ${testConcept.name} @node(labels: ["${testConcept.name}", "Resource"]){ - iri: ID! @unique @alias(property: "uri") - prefLabel: [String!]! - } - `; - await testHelper.initNeo4jGraphQL({ typeDefs }); - }); - - afterAll(async () => { - await testHelper.close(); - }); - - test("should query nested connection", async () => { - const createBibRefQuery = /* GraphQL */ ` - mutation { - ${testBibliographicReference.operations.create}( - input: { - iri: "urn:myiri2" - prefLabel: "Initial label" - isInPublication: { - create: { node: { iri: "new-e", prefLabel: "stuff" } } - } - } - ) { - ${testBibliographicReference.plural} { - iri - prefLabel - isInPublication { - iri - prefLabel - } - } - } - } - `; - const updateBibRefQuery = /* GraphQL */ ` - mutation { - ${testConcept.operations.delete}(where: { iri_EQ: "new-e" }) { - nodesDeleted - } - - ${testBibliographicReference.operations.update}( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel_SET: "Updated Label" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - ${testBibliographicReference.plural} { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - const createBibRefResult = await testHelper.executeGraphQL(createBibRefQuery); - expect(createBibRefResult.errors).toBeUndefined(); - - const bibRefRes = await testHelper.executeCypher(` - MATCH (bibRef:${testBibliographicReference.name})-[r:isInPublication]->(concept:${testConcept.name}) RETURN bibRef.uri as bibRefUri, concept.uri as conceptUri - `); - - expect(bibRefRes.records).toHaveLength(1); - expect(bibRefRes.records[0]?.toObject().bibRefUri as string).toBe("urn:myiri2"); - expect(bibRefRes.records[0]?.toObject().conceptUri as string).toBe("new-e"); - - const updateBibRefResult = await testHelper.executeGraphQL(updateBibRefQuery); - expect(updateBibRefResult.errors).toBeUndefined(); - expect(updateBibRefResult?.data).toEqual({ - [testConcept.operations.delete]: { - nodesDeleted: 1, - }, - [testBibliographicReference.operations.update]: { - [testBibliographicReference.plural]: [ - { - iri: "urn:myiri2", - prefLabel: ["Updated Label"], - isInPublication: [ - { - iri: "new-f", - prefLabel: ["pub"], - }, - ], - }, - ], - }, - }); - - const conceptCount = await testHelper.executeCypher(` - MATCH (bibRef:${testBibliographicReference.name})-[r:isInPublication]->(concept:${testConcept.name}) RETURN bibRef.uri as bibRefUri, COUNT(concept) as conceptCount - `); - - expect(conceptCount.records).toHaveLength(1); - expect(conceptCount.records[0]?.toObject().bibRefUri as string).toBe("urn:myiri2"); - expect((conceptCount.records[0]?.toObject().conceptCount as Integer).toNumber()).toBe(2); - }); -}); diff --git a/packages/graphql/tests/integration/issues/988.int.test.ts b/packages/graphql/tests/integration/issues/988.int.test.ts index 9b964a6f54..7f8e0ac552 100644 --- a/packages/graphql/tests/integration/issues/988.int.test.ts +++ b/packages/graphql/tests/integration/issues/988.int.test.ts @@ -67,7 +67,7 @@ describe("https://github.com/neo4j/graphql/issues/988", () => { test("should query nested connection", async () => { const query = /* GraphQL */ ` - query getSeriesWithRelationFilters($where: ${seriesType.name}Where = { current: true }) { + query getSeriesWithRelationFilters($where: ${seriesType.name}Where = { current_EQ: true }) { ${seriesType.plural}(where: $where) { name current diff --git a/packages/graphql/tests/integration/issues/bind-any.int.test.ts b/packages/graphql/tests/integration/issues/bind-any.int.test.ts index 55bc8f7bce..65cf4192d4 100644 --- a/packages/graphql/tests/integration/issues/bind-any.int.test.ts +++ b/packages/graphql/tests/integration/issues/bind-any.int.test.ts @@ -21,7 +21,7 @@ import { GraphQLError } from "graphql"; import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; -describe("https://github.com/neo4j/graphql/issues/2474", () => { +describe("bind-any", () => { const testHelper = new TestHelper(); let User: UniqueType; @@ -55,10 +55,10 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { users: [${User}!]! @relationship(type: "IS_MEMBER_OF", direction: IN) } - type ${Group} @authorization(validate: [{ operations: [CREATE], when: [AFTER], where: { node: { organization: { users_SOME: { id_EQ: "$jwt.sub" } } } } }]) @node { + type ${Group} @authorization(validate: [{ operations: [CREATE], when: [AFTER], where: { node: { organization_SINGLE: { users_SOME: { id_EQ: "$jwt.sub" } } } } }]) @node { id: String! name: String - organization: ${Organization}! @relationship(type: "HAS_GROUP", direction: IN) + organization: [${Organization}!]! @relationship(type: "HAS_GROUP", direction: IN) } `; @@ -114,10 +114,10 @@ describe("https://github.com/neo4j/graphql/issues/2474", () => { users: [${User}!]! @relationship(type: "IS_MEMBER_OF", direction: IN) } - type ${Group} @authorization(validate: [{ operations: [CREATE], when: [AFTER], where: { node: { organization: { users_ALL: { id_EQ: "$jwt.sub" } } } } }]) @node { + type ${Group} @authorization(validate: [{ operations: [CREATE], when: [AFTER], where: { node: { organization_SINGLE: { users_ALL: { id_EQ: "$jwt.sub" } } } } }]) @node { id: String! name: String - organization: ${Organization}! @relationship(type: "HAS_GROUP", direction: IN) + organization: [${Organization}!]! @relationship(type: "HAS_GROUP", direction: IN) } `; diff --git a/packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts b/packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.int.test.ts similarity index 93% rename from packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts rename to packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.int.test.ts index 7851b8e8cd..3603afed02 100644 --- a/packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts +++ b/packages/graphql/tests/integration/issues/context-variable-not-always-resolved-on-cypher-queries.int.test.ts @@ -41,7 +41,7 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { IRI """ iri: ID! @id @alias(property: "uri") - realizationOf: ${work.name} @relationship(type: "realizationOf", direction: OUT) + realizationOf: [${work.name}!]! @relationship(type: "realizationOf", direction: OUT) } type ${work.name} @node(labels: ["WorkLabel", "$context.tenant", "Resource"]) @@ -82,7 +82,7 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { query { ${expr.plural}( where: { - realizationOf: { + realizationOf_SOME: { hasResourceType_SOME: { iri_EQ: "uri-to-be-found" } @@ -111,7 +111,7 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { expect(gqlResult.errors).toBeFalsy(); expect(gqlResult.data?.[expr.plural]).toIncludeSameMembers([ - { iri: "stuff", realizationOf: { iri: "another-stuff", hasResourceType: [{ iri: "uri-to-be-found" }] } }, + { iri: "stuff", realizationOf: [{ iri: "another-stuff", hasResourceType: [{ iri: "uri-to-be-found" }] }] }, ]); }); }); diff --git a/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts b/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts index 16c6073563..da9c634857 100644 --- a/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts +++ b/packages/graphql/tests/integration/issues/interface-post-multi-create.int.test.ts @@ -38,17 +38,17 @@ describe("Projecting interface relationships following create of multiple nodes" } type ${Person} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Place} implements Entity @node { - id: String! @unique + id: String! name: String! } type ${Interaction} @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) diff --git a/packages/graphql/tests/integration/math.int.test.ts b/packages/graphql/tests/integration/math.int.test.ts index cd3f5a280a..70df9ee16c 100644 --- a/packages/graphql/tests/integration/math.int.test.ts +++ b/packages/graphql/tests/integration/math.int.test.ts @@ -32,16 +32,16 @@ describe("Mathematical operations tests", () => { }); test.each([ - { initialValue: int(0), value: 5, type: "Int", operation: "INCREMENT", expected: 5 }, - { initialValue: int(10), value: 5, type: "Int", operation: "DECREMENT", expected: 5 }, - { initialValue: int(0), value: "5", type: "BigInt", operation: "INCREMENT", expected: "5" }, - { initialValue: int(10), value: "5", type: "BigInt", operation: "DECREMENT", expected: "5" }, - { initialValue: int(10), value: "-5", type: "BigInt", operation: "DECREMENT", expected: "15" }, - { initialValue: 0.0, value: 5.0, type: "Float", operation: "ADD", expected: 5.0 }, - { initialValue: 10.0, value: 5.0, type: "Float", operation: "SUBTRACT", expected: 5.0 }, - { initialValue: 10.0, value: 5.0, type: "Float", operation: "MULTIPLY", expected: 50.0 }, - { initialValue: 10.0, value: -5.0, type: "Float", operation: "MULTIPLY", expected: -50.0 }, - { initialValue: 10.0, value: 5.0, type: "Float", operation: "DIVIDE", expected: 2.0 }, + { initialValue: int(0), value: 5, type: "Int", operation: "add", expected: 5 }, + { initialValue: int(10), value: 5, type: "Int", operation: "subtract", expected: 5 }, + { initialValue: int(0), value: "5", type: "BigInt", operation: "add", expected: "5" }, + { initialValue: int(10), value: "5", type: "BigInt", operation: "subtract", expected: "5" }, + { initialValue: int(10), value: "-5", type: "BigInt", operation: "subtract", expected: "15" }, + { initialValue: 0.0, value: 5.0, type: "Float", operation: "add", expected: 5.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "subtract", expected: 5.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "multiply", expected: 50.0 }, + { initialValue: 10.0, value: -5.0, type: "Float", operation: "multiply", expected: -50.0 }, + { initialValue: 10.0, value: 5.0, type: "Float", operation: "divide", expected: 2.0 }, ])( "Simple operations on numerical fields: on $type, $operation($initialValue, $value) should return $expected", async ({ initialValue, type, value, operation, expected }) => { @@ -62,7 +62,7 @@ describe("Mathematical operations tests", () => { const query = /* GraphQL */ ` mutation($id: ID, $value: ${type}) { - ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_${operation}: $value}) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers: { ${operation}: $value }}) { ${movie.plural} { id viewers @@ -98,24 +98,24 @@ describe("Mathematical operations tests", () => { initialValue: int(largestSafeSigned32BitInteger), value: largestSafeSigned32BitInteger, type: "Int", - operation: "INCREMENT", + operation: "add", expectedError: "overflow", }, { initialValue: int(largestSafeSigned64BitBigInt), value: largestSafeSigned64BitBigInt, type: "BigInt", - operation: "INCREMENT", + operation: "add", expectedError: "overflow", }, { initialValue: Number.MAX_VALUE, value: Number.MAX_VALUE, type: "Float", - operation: "ADD", + operation: "add", expectedError: "overflow", }, - { initialValue: 10.0, value: 0.0, type: "Float", operation: "DIVIDE", expectedError: "division by zero" }, + { initialValue: 10.0, value: 0.0, type: "Float", operation: "divide", expectedError: "division by zero" }, ])( "Should raise an error in case of $expectedError on $type, initialValue: $initialValue, value: $value", async ({ initialValue, type, value, operation, expectedError }) => { @@ -135,7 +135,7 @@ describe("Mathematical operations tests", () => { const query = /* GraphQL */ ` mutation($id: ID, $value: ${type}) { - ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers_${operation}: $value}) { + ${movie.operations.update}(where: { id_EQ: $id }, update: {viewers: {${operation}: $value }}) { ${movie.plural} { id viewers @@ -321,7 +321,7 @@ describe("Mathematical operations tests", () => { { update: { node: { - viewers_INCREMENT: $value + viewers: {add: $value} } } } @@ -403,7 +403,7 @@ describe("Mathematical operations tests", () => { { update: { node: { - viewers_INCREMENT: $value + viewers: {add: $value } } } } @@ -542,7 +542,7 @@ describe("Mathematical operations tests", () => { { update: { edge: { - pay_ADD: $payIncrement + pay: {add: $payIncrement} } } } @@ -628,8 +628,7 @@ describe("Mathematical operations tests", () => { { update: { edge: { - pay_ADD: $payIncrement - pay_SET: $payIncrement + pay: {add: $payIncrement, set: $payIncrement} } } } @@ -669,10 +668,10 @@ describe("Mathematical operations tests", () => { }); expect(gqlResult.errors).toBeDefined(); - + const relationshipType = `${movie.name}ActorsRelationship`; expect(gqlResult.errors).toEqual([ - new GraphQLError(`Conflicting modification of [[pay_SET]], [[pay_ADD]] on type ${relationshipType}`), + new GraphQLError(`Conflicting modification of field pay: [[set]], [[add]] on type ${relationshipType}`), ]); const storedValue = await testHelper.executeCypher( ` diff --git a/packages/graphql/tests/integration/migration-warnings/mandatory-node.test.ts b/packages/graphql/tests/integration/migration-warnings/mandatory-node.test.ts deleted file mode 100644 index 7480c1597f..0000000000 --- a/packages/graphql/tests/integration/migration-warnings/mandatory-node.test.ts +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src/classes"; - -describe("mandatory @node warnings", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); - - afterEach(() => { - warn.mockReset(); - }); - - test("type without @node directive should produces a warn message", async () => { - const typeDefsWithoutNodeDirective = /* GraphQL */ ` - type User { - id: ID! - firstName: String! - lastName: String! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithoutNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).toHaveBeenCalledWith( - "Future library versions will require marking all types representing Neo4j nodes with the @node directive." - ); - }); - - test("multiple types without @node directive should warn only once", async () => { - const typeDefsWithoutNodeDirective = /* GraphQL */ ` - type User { - id: ID! - firstName: String! - lastName: String! - } - type Movie { - id: ID! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithoutNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).toHaveBeenCalledTimes(1); - }); - - test("type with @node directive should not produces a warn message", async () => { - const typeDefsWithNodeDirective = /* GraphQL */ ` - type User @node { - id: ID! - firstName: String! - lastName: String! - } - type Movie @node(labels: ["Film"]) { - id: ID! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).not.toHaveBeenCalled(); - }); - - test("type with @jwt directive should not produces a warn message", async () => { - const typeDefsWithNodeDirective = /* GraphQL */ ` - type User @node { - id: ID! - firstName: String! - lastName: String! - } - - type JWT @jwt { - roles: [String!]! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).not.toHaveBeenCalled(); - }); - - test("type with @relationshipProperties directive should not produces a warn message", async () => { - const typeDefsWithNodeDirective = /* GraphQL */ ` - type Series @node { - title: String! - cost: Float! - episodes: Int! - } - type ActedIn @relationshipProperties { - screenTime: Int! - } - type Actor @node { - name: String! - actedIn: [Series!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).not.toHaveBeenCalled(); - }); - - test("type implementing an interface should not produces a warn message when the @node is being used", async () => { - const typeDefsWithNodeDirective = /* GraphQL */ ` - interface Person { - name: String! - } - - type Actor implements Person @node { - name: String! - role: String! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).not.toHaveBeenCalled(); - }); - - test("extended type should not produces a warn message when the @node is being used", async () => { - const typeDefsWithNodeDirective = /* GraphQL */ ` - type Actor { - name: String! - role: String! - } - extend type Actor @node { - age: Int! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefsWithNodeDirective, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/graphql/tests/integration/migration-warnings/private-directive.test.ts b/packages/graphql/tests/integration/migration-warnings/private-directive.test.ts deleted file mode 100644 index 903879f327..0000000000 --- a/packages/graphql/tests/integration/migration-warnings/private-directive.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src/classes"; - -describe("deprecated @unique warnings", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); - - afterEach(() => { - warn.mockReset(); - }); - - test("warning on unique usage", async () => { - const typeDefs = /* GraphQL */ ` - type User @node { - id: ID! @private - firstName: String! - } - - type Movie @node { - id: ID! @private - title: String! - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefs, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).toHaveBeenCalledWith("Future library versions will not support @private directive."); - }); -}); diff --git a/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts b/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts deleted file mode 100644 index 896f200614..0000000000 --- a/packages/graphql/tests/integration/migration-warnings/unique-directive.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src/classes"; - -describe("deprecated @unique warnings", () => { - let warn: jest.SpyInstance; - - beforeEach(() => { - warn = jest.spyOn(console, "warn").mockImplementation(() => {}); - }); - - afterEach(() => { - warn.mockReset(); - }); - - test("warning on unique usage", async () => { - const typeDefs = /* GraphQL */ ` - type User @node { - id: ID! @unique - firstName: String! - } - - type Movie @node { - id: ID! @unique - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs: typeDefs, - validate: true, - }); - await neoSchema.getSchema(); - expect(warn).toHaveBeenCalledWith("Future library versions will not support @unique directive."); - }); -}); diff --git a/packages/graphql/tests/integration/pagination/limit-required.int.test.ts b/packages/graphql/tests/integration/pagination/limit-required.int.test.ts new file mode 100644 index 0000000000..5a7a1ff210 --- /dev/null +++ b/packages/graphql/tests/integration/pagination/limit-required.int.test.ts @@ -0,0 +1,856 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { generate } from "randomstring"; +import { TestHelper } from "../../utils/tests-helper"; +import { GraphQLError } from "graphql"; + +const testLabel = generate({ charset: "alphabetic" }); + +describe("Limit required", () => { + const testHelper = new TestHelper(); + const movieType = testHelper.createUniqueType("Movie"); + const seriesType = testHelper.createUniqueType("Series"); + const actorType = testHelper.createUniqueType("Actor"); + + const typeDefs = /* GraphQL */ ` + interface Production { + id: ID! + title: String! + } + type ${movieType} implements Production @node { + id: ID! + title: String! + runtime: Int! + actors: [${actorType}!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + type ${seriesType} implements Production @node { + id: ID! + title: String! + episodes: Int! + } + interface Person { + id: ID! + name: String! + actedIn: [Production!]! @declareRelationship + contact: [Contact!]! @declareRelationship + } + union Contact = Telephone | Email + type Telephone @node { + number: String! + } + type Email @node { + address: String! + } + type ${actorType} implements Person @node { + id: ID! + name: String! + movies: [${movieType}!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + contact: [Contact!]! @relationship(type: "HAS_CONTACT", direction: OUT) + } + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + + const movies = [ + { + id: generate({ charset: "alphabetic" }), + title: "A", + runtime: 400, + }, + { + id: generate({ charset: "alphabetic" }), + title: "B", + runtime: 300, + }, + ] as const; + + const series = [ + { + id: generate({ charset: "alphabetic" }), + title: "C", + episodes: 200, + }, + { + id: generate({ charset: "alphabetic" }), + title: "D", + episodes: 100, + }, + ] as const; + + const actors = [ + { + id: generate({ charset: "alphabetic" }), + name: `A${generate({ charset: "alphbetic" })}`, + screenTime: { + [movies[0].id]: 2, + [movies[1].id]: 1, + [series[0].id]: 6, + [series[1].id]: 4, + }, + emailAddress: "a@mail.com", + }, + { + id: generate({ charset: "alphabetic" }), + name: `B${generate({ charset: "alphbetic" })}`, + screenTime: { + [movies[1].id]: 1, + }, + phoneNumber:"123456", + }, + ] as const; + + beforeAll(async () => { + await testHelper.initNeo4jGraphQL({ + typeDefs, + features: { + limitRequired: true, + }, + }); + + await testHelper.executeCypher( + ` + CREATE (m1:${movieType}:${testLabel}) SET m1 = $movies[0] + CREATE (m2:${movieType}:${testLabel}) SET m2 = $movies[1] + CREATE (s1:${seriesType}:${testLabel}) SET s1 = $series[0] + CREATE (s2:${seriesType}:${testLabel}) SET s2 = $series[1] + + CREATE (a1:${actorType}:${testLabel}) SET a1.id = $actors[0].id, a1.name = $actors[0].name + CREATE (a2:${actorType}:${testLabel}) SET a2.id = $actors[1].id, a2.name = $actors[1].name + + MERGE (a1)-[:ACTED_IN {screenTime: $actors[0].screenTime[m1.id]}]->(m1) + MERGE (a1)-[:ACTED_IN {screenTime: $actors[0].screenTime[m1.id]}]->(m1) + MERGE (a1)-[:ACTED_IN {screenTime: $actors[0].screenTime[m2.id]}]->(m2)<-[:ACTED_IN {screenTime: $actors[1].screenTime[m2.id]}]-(a2) + MERGE (s1)<-[:ACTED_IN {screenTime: $actors[0].screenTime[s1.id]}]-(a1)-[:ACTED_IN {screenTime: $actors[0].screenTime[s2.id]}]->(s2) + + MERGE (mail: Email {address: $actors[0].emailAddress}) + MERGE (phone: Phone {number: $actors[1].phoneNumber}) + MERGE (a1)-[:HAS_CONTACT]->(mail) + MERGE (a2)-[:HAS_CONTACT]->(phone) + `, + { movies, series, actors } + ); + }); + + afterAll(async () => { + await testHelper.close(); + }); + + describe("simple query field on object types", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.plural}(limit: 1, sort: {title: ASC}) { + title + actors(limit: 1, sort: {name: ASC}) { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[movieType.plural]).toEqual([{ actors: [{ name: actors[0].name }], title: "A" }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.plural} { + actors { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${movieType.plural}" argument "limit" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "actors" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.plural}(limit: 1) { + actors { + name + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "actors" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("simple query field on object types - relationship to interface", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural}(limit: 1, sort: {name: ASC}) { + name + actedIn(limit: 1, sort: {title: ASC}) { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[actorType.plural]).toEqual([{ actedIn: [{ title: "A"}], name: actors[0].name }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural} { + actedIn { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${actorType.plural}" argument "limit" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "actedIn" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural}(limit: 1) { + actedIn { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "actedIn" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("simple query field on object types - relationship to union", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural}(limit: 1, sort: {name: ASC}) { + name + contact(limit: 1) { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[ actorType.plural]).toEqual([{ contact: [{ address: "a@mail.com"}], name: actors[0].name }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural} { + contact{ + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${actorType.plural}" argument "limit" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "contact" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.plural}(limit: 1) { + contact { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "contact" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("simple query field on object types - interface relationship to another interface", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + people(limit: 1, sort: {name: ASC}) { + name + actedIn(limit: 1, sort: {title: ASC}) { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)["people"]).toEqual([{ actedIn: [{ title: "A"}], name: actors[0].name }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + people { + actedIn { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "people" argument "limit" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "actedIn" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + people(limit: 1) { + actedIn { + title + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "actedIn" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("simple query field on object types - interface relationship to union", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + people(limit: 1, sort: {name: ASC}) { + name + contact(limit: 1) { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)["people"]).toEqual([{ contact: [{ address: "a@mail.com"}], name: actors[0].name }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + people { + contact{ + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "people" argument "limit" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "contact" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + people(limit: 1) { + contact { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "contact" argument "limit" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("connection query field on object types", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.operations.connection}(first: 1, sort: {title: ASC}) { + edges { + node { + title + actorsConnection(first: 1, sort: {node: {name: ASC} }) { + edges { + node { + name + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[movieType.operations.connection]["edges"]).toEqual([ + { + node: { actorsConnection: {edges: [{ node: { name: actors[0].name }}]}, title: "A" }, + }, + ]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.operations.connection} { + edges { + node { + title + actorsConnection { + edges { + node { + name + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + console.log(gqlResult.errors, 0,2) + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError("Field \"actorsConnection\" argument \"first\" of type \"Int!\" is required, but it was not provided."), + new GraphQLError(`Field "${movieType.operations.connection}" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + ${movieType.operations.connection}(first: 1, sort: {title: ASC}) { + edges { + node { + title + actorsConnection { + edges { + node { + name + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError("Field \"actorsConnection\" argument \"first\" of type \"Int!\" is required, but it was not provided."), + ]); + }); + }); + describe("connection query field on object types - relationship to union", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.operations.connection}(first: 1, sort: {name: ASC}) { + edges { + node { + name + contactConnection(first: 1) { + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)[ actorType.operations.connection]["edges"]).toEqual([{ node: { contactConnection: {edges: [{node: { address: "a@mail.com"}}]}, name: actors[0].name }}]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.operations.connection} { + edges { + node { + name + contactConnection { + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "${actorType.operations.connection}" argument "first" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "contactConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + ${actorType.operations.connection}(first: 1) { + edges { + node { + name + contactConnection { + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "contactConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("connection query field on object types - interface relationship to another interface", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection(first: 1, sort: { name: ASC }) { + edges { + node { + name + actedInConnection(first: 1, sort: { node: { title: ASC } }) { + edges { + node { + title + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)["peopleConnection"]["edges"]).toEqual([{node: { actedInConnection: {edges: [{node: {title: "A"}}]}, name: actors[0].name }}]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection { + edges { + node { + actedInConnection { + edges { + node { + title + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "peopleConnection" argument "first" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "actedInConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection(first: 1) { + edges { + node { + actedInConnection { + edges { + node { + title + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "actedInConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); + describe("connection query field on object types - interface relationship to union", () => { + test("limit argument provided", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection(first: 1, sort: { name: ASC }) { + edges { + node { + name + contactConnection(first: 1) { + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toBeUndefined(); + expect((gqlResult.data as any)["peopleConnection"]["edges"]).toEqual([ + { + node: { + name: actors[0].name, + contactConnection: { + edges: [ + { node: { address: "a@mail.com"}}, + ], + }, + }, + }]); + }); + + test("limit argument not provided", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection { + edges { + node { + contactConnection{ + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(2); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "peopleConnection" argument "first" of type "Int!" is required, but it was not provided.`), + new GraphQLError(`Field "contactConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + + test("limit argument provided only root level", async () => { + const query = /* GraphQL */ ` + query { + peopleConnection(first: 1) { + edges { + node { + contactConnection { + edges { + node { + ... on Telephone { + number + } + ... on Email { + address + } + } + } + } + } + } + } + } + `; + + const gqlResult = await testHelper.executeGraphQL(query); + + expect(gqlResult.errors).toHaveLength(1); + expect(gqlResult.errors).toIncludeSameMembers([ + new GraphQLError(`Field "contactConnection" argument "first" of type "Int!" is required, but it was not provided.`), + ]); + }); + }); +}); + diff --git a/packages/graphql/tests/integration/pagination/pagination.int.test.ts b/packages/graphql/tests/integration/pagination/pagination.int.test.ts index b5366c9a8b..7a1003f4ff 100644 --- a/packages/graphql/tests/integration/pagination/pagination.int.test.ts +++ b/packages/graphql/tests/integration/pagination/pagination.int.test.ts @@ -115,7 +115,6 @@ describe("Pagination", () => { beforeAll(async () => { await testHelper.initNeo4jGraphQL({ typeDefs, - features: { excludeDeprecatedFields: { deprecatedOptionsArgument: true } }, }); await testHelper.executeCypher( diff --git a/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts b/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts deleted file mode 100644 index ba5de0e849..0000000000 --- a/packages/graphql/tests/integration/relationship-properties/connect-overwrite.int.test.ts +++ /dev/null @@ -1,2161 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { DocumentNode } from "graphql"; -import { gql } from "graphql-tag"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("Relationship properties - connect with and without `overwrite` argument", () => { - const testHelper = new TestHelper(); - let typeDefs: DocumentNode; - - let typeActor: UniqueType; - let typeMovie: UniqueType; - - afterEach(async () => { - await testHelper.close(); - }); - - describe("Effect on other relationships", () => { - let movieTitle: string; - let actorName: string; - let directorName: string; - let year: number; - let screenTime: number; - let screenTimeUpdate: number; - - beforeEach(async () => { - typeActor = testHelper.createUniqueType("Actor"); - typeMovie = testHelper.createUniqueType("Movie"); - - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - directors: [${typeActor.name}!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) - actors: [${typeActor.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${typeActor.name} @node { - name: String! - movies: [ ${typeMovie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - directed: [ ${typeMovie.name}!]! @relationship(type: "DIRECTED", properties: "Directed", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - type Directed @relationshipProperties { - year: Int! - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - movieTitle = "Movie 1"; - actorName = "Actor 1"; - directorName = "Director 1"; - year = 2010; - screenTime = 123; - screenTimeUpdate = 134; - - await testHelper.executeCypher( - ` - CREATE (:${typeActor.name} {name:$actorName}) - CREATE (:${typeActor.name} {name:$directorName}) - `, - { actorName: "Actor 1", directorName: "Director 1" } - ); - }); - - test("should return error when overwrite is false, other field does not get updated because of error even if it's first", async () => { - await testHelper.executeCypher( - ` - MATCH (actor:${typeActor.name} {name: $actorName}) - MATCH (director:${typeActor.name} {name: $directorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:ACTED_IN { screenTime: $screenTime }]->(m) - MERGE (director)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, directorName, screenTime, year } - ); - - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!, $year: Int!, $directorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { - connect: { - where: { node: { name_EQ: $directorName } }, - edge: { year: $year } - } - }, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, directorName, year: 2011, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - const directorOverwritten = await testHelper.executeCypher( - `MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (d:${typeActor.name} {name: $directorName}) - RETURN d `, - { movieTitle, year: 2011, directorName } - ); - expect(directorOverwritten.records).toHaveLength(0); - }); - - test("should return error when overwrite is false, other field does not get connected because of error", async () => { - await testHelper.executeCypher( - ` - MATCH (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:ACTED_IN { screenTime: $screenTime }]->(m) - `, - { movieTitle, actorName, screenTime } - ); - - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!, $year: Int!, $directorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { - connect: { - where: { node: { name_EQ: $directorName } }, - edge: { year: $year }, - overwrite: false - } - }, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, directorName, year, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - const directorOverwritten = await testHelper.executeCypher( - `MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (d:${typeActor.name} {name: $directorName}) - RETURN d `, - { movieTitle, year: 2011, directorName } - ); - expect(directorOverwritten.records).toHaveLength(0); - }); - }); - - describe("Relationships of type 1:n", () => { - let movieTitle: string; - let movieOtherTitle: string; - let actorName: string; - let screenTime: number; - let screenTimeUpdate: number; - let screenTimeOther: number; - - beforeEach(async () => { - typeActor = testHelper.createUniqueType("Actor"); - typeMovie = testHelper.createUniqueType("Movie"); - - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - actors: ${typeActor.name}! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${typeActor.name} @node { - name: String! - movies: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - movieTitle = "Movie 1"; - movieOtherTitle = "Movie 2"; - actorName = "Actor 1"; - screenTime = 123; - screenTimeUpdate = 134; - screenTimeOther = 156; - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:ACTED_IN { screenTime: $screenTime }]->(m) - `, - { movieTitle, actorName, screenTime } - ); - }); - - // update + update + connect - test("should overwrite existing relationship with new properties: connect in nested update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - expect((gqlResultUpdate.data as any)?.[typeMovie.operations.update][typeMovie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [{ properties: { screenTime: screenTimeUpdate }, node: { name: actorName } }], - }, - }, - ]); - - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - const neo4jResult = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResult.records).toHaveLength(1); - }); - - test("should return error because overwrite set to false: connect in nested update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - // update + connect - test("should return error because overwrite set to false, when relationship field has cardinality 1: connect in update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect:{ - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties when relationship field has cardinality 1: connect in update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: true - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - expect((gqlResultUpdate.data as any)?.[typeMovie.operations.update][typeMovie.plural]).toEqual([ - { - title: movieTitle, - actorsConnection: { - edges: [{ properties: { screenTime: screenTimeUpdate }, node: { name: actorName } }], - }, - }, - ]); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - test("should return error because overwrite set to false, when relationship field has cardinality n: connect in update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeActor.operations.update}( - where: { - name_EQ: $actorName - }, - update: { - movies: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - - ) { - ${typeActor.plural} { - name - moviesConnection { - edges { - properties { - screenTime - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.movies required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties when relationship field has cardinality n: connect in update", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeActor.operations.update}( - where: { - name_EQ: $actorName - }, - update: { - movies: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - } - } - } - - ) { - ${typeActor.plural} { - name - moviesConnection { - edges { - properties { - screenTime - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - // create + connect - test("should return error because overwrite set to false: connect in create", async () => { - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeActor.operations.create}( - input: [ - { - name: $actorName, - movies: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - } - ] - ) { - ${typeActor.plural} { - name - moviesConnection { - edges { - properties { - screenTime - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - // nested connect-connect - test("should return error because overwrite set to false, when relationship field has cardinality n: nested connect in create", async () => { - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - overwrite: false - }] - } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, movieOtherTitle }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.movies required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties, when relationship field has cardinality n: nested connect in create", async () => { - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - }] - } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, movieOtherTitle }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - test("should overwrite existing relationship with new properties when relationship field has cardinality 1: nested connect in create", async () => { - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - connect: { - actors: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - } - } - }] - } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, movieOtherTitle }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - test("should return error because overwrite set to false, when relationship field has cardinality 1: nested connect in create", async () => { - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTime }, - connect: { - actors: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - overwrite: false - } - } - }] - } - } - } - } - ] - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, movieOtherTitle }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude(`${typeMovie.name}.actors required exactly once`); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - // update connect-connect - test("should overwrite existing relationship with new properties: nested connect in nested update", async () => { - const update = ` - mutation($screenTimeOther: Int!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTimeOther }, - }] - } - } - } - } - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, screenTimeOther }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwrittenOnce = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwrittenOnce.records).toHaveLength(0); - const neo4jResultOverwrittenTwice = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeOther, - actorName, - }); - expect(neo4jResultOverwrittenTwice.records).toHaveLength(1); - }); - - test("should return error because overwrite set to false: nested connect in nested update", async () => { - const update = ` - mutation($screenTimeOther: Int!, $movieTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTime }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTimeOther }, - overwrite: false - }] - } - } - } - } - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, screenTimeOther }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.movies required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwrittenOnce = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - }); - expect(neo4jResultOverwrittenOnce.records).toHaveLength(0); - const neo4jResultOverwrittenTwice = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeOther, - actorName, - }); - expect(neo4jResultOverwrittenTwice.records).toHaveLength(0); - }); - - // update - create - connect - test("should return error because overwrite set to false: update-create-nested connect", async () => { - const update = ` - mutation($screenTimeOther: Int!, $movieTitle: String!, $movieOtherTitle: String!, $screenTime: Int!, $actorName: String!) { - ${typeActor.operations.update}( - where: { - name_EQ: $actorName - }, - update: { - movies: { - create:[ - { - edge: { screenTime: $screenTime }, - node: { - title: $movieOtherTitle - actors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { screenTime: $screenTimeOther }, - connect: { - movies: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { screenTime: $screenTimeOther }, - overwrite: false - }] - }, - } - } - } - } - ]} - } - ) { - ${typeActor.plural} { - name - moviesConnection { - edges { - properties { - screenTime - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { - movieTitle, - actorName, - screenTime: screenTimeUpdate, - screenTimeOther, - movieOtherTitle, - }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.movies required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - }); - - describe("Relationships type 1:n with @unique", () => { - let movieTitle: string; - let actorName: string; - let screenTime: number; - let screenTimeUpdate: number; - - beforeEach(async () => { - typeActor = testHelper.createUniqueType("Actor"); - typeMovie = testHelper.createUniqueType("Movie"); - - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - actors: ${typeActor.name}! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ${typeActor.name} @node { - name: String! - id: Int! @unique - movies: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - movieTitle = "Movie 1"; - actorName = "Actor 1"; - screenTime = 123; - screenTimeUpdate = 134; - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:ACTED_IN { screenTime: $screenTime }]->(m) - `, - { movieTitle, actorName, screenTime } - ); - }); - - // update + update + connectOrCreate - test("update with connectOrCreate is a no-op when relationship field has cardinality 1, overwrite not an option", async () => { - const actorId = 1; - - await testHelper.executeCypher( - ` - MATCH (actor:${typeActor.name} {name: $actorName}) - SET actor.id=$actorId - `, - { actorName, actorId } - ); - const update = ` - mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!, $actorId: Int!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - actors: { - connectOrCreate: { - where: { node: { id_EQ: $actorId } }, - onCreate: { edge: { screenTime: $screenTime }, node: { name: $actorName, id: $actorId } }, - } - } - } - - ) { - ${typeMovie.plural} { - title - actorsConnection { - edges { - properties { - screenTime - } - node { - name - id - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:ACTED_IN {screenTime: $screenTime}]- - (:${typeActor.name} {name: $actorName, id: $actorId}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, actorName, screenTime: screenTimeUpdate, actorId }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime, - actorName, - actorId, - }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - screenTime: screenTimeUpdate, - actorName, - actorId, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - }); - - describe("Relationships of type n:n", () => { - let movieTitle: string; - let movieOtherTitle: string; - let actorName: string; - let actorNameOther: string; - let year: number; - let yearOther: number; - - beforeEach(async () => { - typeActor = testHelper.createUniqueType("Actor"); - typeMovie = testHelper.createUniqueType("Movie"); - - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - directors: [${typeActor.name}!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) - } - - type ${typeActor.name} @node { - name: String! - directed: [${typeMovie.name}!]! @relationship(type: "DIRECTED", properties: "Directed", direction: OUT) - } - - type Directed @relationshipProperties { - year: Int! - } - `; - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - - movieTitle = "Movie 1"; - movieOtherTitle = "Movie 2"; - actorName = "Actor 1"; - actorNameOther = "Actor 2"; - year = 2010; - yearOther = 2011; - }); - - // update + update + connect - test("should return error because overwrite set to false", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - overwrite: false - } - } - } - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeMovie.name}.directors required exactly once for a specific ${typeActor.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { - connect: { - where: { node: { name_EQ: $actorName } }, - edge: { year: $year } - } - } - } - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - // update + connect - test("should overwrite existing relationship with new properties: connect in update", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!) { - ${typeActor.operations.update}( - where: { - name_EQ: $actorName - }, - update: { - directed: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year } - } - } - } - ) { - ${typeActor.plural} { - name - directedConnection { - edges { - properties { - year - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - test("should return error because overwrite set to false: connect in update", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!) { - ${typeActor.operations.update}( - where: { - name_EQ: $actorName - }, - update: { - directed: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year }, - overwrite: false - } - } - } - ) { - ${typeActor.plural} { - name - directedConnection { - edges { - properties { - year - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.directed required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - // create + connect - test("should return error because overwrite set to false: connect in create", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!, $yearOther: Int!) { - ${typeActor.operations.create}( - input: [ - { - name: $actorName - directed: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year }, - connect: { - directors: { - where: { node: { name_EQ: $actorName } }, - edge: { year: $yearOther }, - overwrite: false - } - } - } - } - } - ] - ) { - ${typeActor.plural} { - name - directedConnection { - edges { - properties { - year - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year, yearOther, actorName: actorNameOther }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeMovie.name}.directors required exactly once for a specific ${typeActor.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties: connect in create", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieTitle: String!, $actorName: String!, $year: Int!, $yearOther: Int!) { - ${typeActor.operations.create}( - input: [ - { - name: $actorName - directed: { - connect: { - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year }, - connect: { - directors: { - where: { node: { name_EQ: $actorName } }, - edge: { year: $yearOther } - } - } - } - } - } - ] - ) { - ${typeActor.plural} { - name - directedConnection { - edges { - properties { - year - } - node { - title - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, year, yearOther, actorName: actorNameOther }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { - movieTitle, - year, - actorName: actorNameOther, - }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName: actorNameOther, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - // nested connect-connect - test("should return error because overwrite set to false: nested connect in create", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $year: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - directors: { - connect: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - connect: { - directed: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year }, - connect: { - directors: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - overwrite: false - }] - } - }] - } - }] - } - } - ] - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, movieOtherTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeMovie.name}.directors required exactly once for a specific ${typeActor.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties: nested connect in create", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($movieOtherTitle: String!, $movieTitle: String!, $year: Int!, $actorName: String!) { - ${typeMovie.operations.create}( - input: [ - { - title: $movieOtherTitle, - directors: { - connect: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - connect: { - directed: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $year }, - connect: { - directors: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year } - }] - } - }] - } - }] - } - } - ] - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, movieOtherTitle, year: yearOther, actorName }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - - // update connect-connect - test("should return error because overwrite set to false on last level: nested connect in update", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($yearOther: Int!, $movieTitle: String!, $year: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { connect: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - connect: { - directed: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $yearOther }, - overwrite: false - }] - } - }]} - } - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, movieOtherTitle, year, yearOther, actorName }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeActor.name}.directed required exactly once for a specific ${typeMovie.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should return error because overwrite set to false on inner level: nested connect in update", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($yearOther: Int!, $movieTitle: String!, $year: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { connect: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - overwrite: false - connect: { - directed: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $yearOther }, - }] - } - }]} - } - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, movieOtherTitle, year, yearOther, actorName }, - }); - expect(gqlResultUpdate.errors?.[0]?.toString()).toInclude( - `${typeMovie.name}.directors required exactly once for a specific ${typeActor.name}` - ); - expect(gqlResultUpdate.data).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(1); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(0); - }); - - test("should overwrite existing relationship with new properties: nested connect in update", async () => { - await testHelper.executeCypher( - ` - CREATE (actor:${typeActor.name} {name: $actorName}) - CREATE (m:${typeMovie.name} { title: $movieTitle }) - MERGE (actor)-[:DIRECTED { year: $year }]->(m) - `, - { movieTitle, actorName, year } - ); - const update = ` - mutation($yearOther: Int!, $movieTitle: String!, $year: Int!, $actorName: String!) { - ${typeMovie.operations.update}( - where: { - title_EQ: $movieTitle - }, - update: { - directors: { connect: [{ - where: { node: { name_EQ: $actorName } }, - edge: { year: $year }, - connect: { - directed: [{ - where: { node: { title_EQ: $movieTitle } }, - edge: { year: $yearOther }, - }] - } - }]} - } - ) { - ${typeMovie.plural} { - title - directorsConnection { - edges { - properties { - year - } - node { - name - } - } - } - } - } - } - `; - - const cypher = ` - MATCH (m:${typeMovie.name} {title: $movieTitle}) - <-[:DIRECTED {year: $year}]- - (:${typeActor.name} {name: $actorName}) - RETURN m - `; - - const gqlResultUpdate = await testHelper.executeGraphQL(update, { - variableValues: { movieTitle, movieOtherTitle, year, yearOther, actorName }, - }); - expect(gqlResultUpdate.errors).toBeFalsy(); - - const neo4jResultInitial = await testHelper.executeCypher(cypher, { movieTitle, year, actorName }); - expect(neo4jResultInitial.records).toHaveLength(0); - const neo4jResultOverwritten = await testHelper.executeCypher(cypher, { - movieTitle, - year: yearOther, - actorName, - }); - expect(neo4jResultOverwritten.records).toHaveLength(1); - }); - }); -}); diff --git a/packages/graphql/tests/integration/relationship-properties/connect-union.int.test.ts b/packages/graphql/tests/integration/relationship-properties/connect-union.int.test.ts index 7e7f220b10..cafc808b7a 100644 --- a/packages/graphql/tests/integration/relationship-properties/connect-union.int.test.ts +++ b/packages/graphql/tests/integration/relationship-properties/connect-union.int.test.ts @@ -170,4 +170,58 @@ describe("Relationship properties - connect on union", () => { const neo4jResult = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); expect(neo4jResult.records).toHaveLength(1); }); + + test("should update an actor while connecting an existing relationship that has properties(with Union)", async () => { + const movieTitle = generate({ charset: "alphabetic" }); + const actorName = generate({ charset: "alphabetic" }); + const screenTime = Math.floor((Math.random() * 1e3) / Math.random()); + + const source = /* GraphQL */ ` + mutation($movieTitle: String!, $screenTime: Int!, $actorName: String!) { + ${Actor.operations.update}( + where: { name_EQ: $actorName } + update: { + actedIn: { + ${Movie}: { + connect: { + where: { node: { title_EQ: $movieTitle } } + edge: { screenTime: $screenTime } + } + } + } + } + ) { + ${Actor.plural} { + name + } + } + } + `; + + await testHelper.executeCypher( + ` + CREATE (:${Movie} {title:$movieTitle}) + CREATE (:${Actor} {name:$actorName})-[:ACTED_IN {screenTime: 0}]->(:${Movie} {title: $movieTitle}) + `, + { movieTitle, actorName } + ); + + const gqlResult = await testHelper.executeGraphQL(source, { + variableValues: { movieTitle, actorName, screenTime }, + }); + expect(gqlResult.errors).toBeFalsy(); + expect((gqlResult.data as any)[Actor.operations.update][Actor.plural]).toEqual([ + { + name: actorName, + }, + ]); + + const cypher = ` + MATCH (a:${Actor} {name: $actorName})-[:ACTED_IN {screenTime: $screenTime}]->(:${Movie} {title: $movieTitle}) + RETURN a + `; + + const neo4jResult = await testHelper.executeCypher(cypher, { movieTitle, screenTime, actorName }); + expect(neo4jResult.records).toHaveLength(2); + }); }); diff --git a/packages/graphql/tests/integration/rfcs/rfc-003.int.test.ts b/packages/graphql/tests/integration/rfcs/rfc-003.int.test.ts deleted file mode 100644 index 7c9018d54d..0000000000 --- a/packages/graphql/tests/integration/rfcs/rfc-003.int.test.ts +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { generate } from "randomstring"; -import type { UniqueType } from "../../utils/graphql-types"; -import { TestHelper } from "../../utils/tests-helper"; - -describe("integration/rfc/003", () => { - const testHelper = new TestHelper(); - let Director: UniqueType; - let Movie: UniqueType; - let CoDirector: UniqueType; - let Address: UniqueType; - - beforeEach(() => { - Director = testHelper.createUniqueType("Director"); - Movie = testHelper.createUniqueType("Movie"); - CoDirector = testHelper.createUniqueType("CoDirector"); - Address = testHelper.createUniqueType("Address"); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - describe("one-to-one", () => { - describe("create", () => { - test("should throw when creating node without a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.create}(input: [{ id: "${movieId}" }]) { - info { - nodesCreated - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director required exactly once`); - }); - - describe("nested mutations", () => { - test("should throw when creating node without a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Address} @node { - street: String! - } - - type ${Director} @node { - id: ID! - address: ${Address}! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.create}(input: [{ id: "${movieId}", director: { create: { node: { id: "${directorId}" } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Director}.address required exactly once`); - }); - }); - }); - - describe("update", () => { - test("should throw error when updating a node without a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}(where: { id_EQ: "${movieId}" }, update: { id_SET: "${movieId}" }) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director required exactly once`); - }); - - describe("nested mutations", () => { - test("should throw when creating node without relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Address} @node { - street: String! - } - - type ${Director} @node { - id: ID! - address: ${Address}! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" } - update: { director: { update: { node: { id_SET: "${directorId}" } } } } - ) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"})<-[:DIRECTED]-(:${Director} { id: "${directorId}" }) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Director}.address required exactly once`); - }); - - test("should throw error when creating a node without a required relationship through a nested mutation", async () => { - const typeDefs = /* GraphQL */ ` - type ${Address} @node { - street: String! - } - - type ${Director} @node { - id: ID! - address: ${Address}! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = /* GraphQL */ ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" } - update: { director: { create: { node: { id: "${directorId}" } } } } - ) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Director}.address required exactly once`); - }); - }); - }); - - describe("delete", () => { - describe("nested mutations", () => { - test("should throw error when deleting a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${CoDirector} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - coDirector: ${CoDirector} @relationship(type: "CO_DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" }, - update: { director: { delete: { where: { node: { id_EQ: "${directorId}" } } } } } - ) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"})<-[:DIRECTED]-(:${Director} {id: "${directorId}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director required exactly once`); - }); - }); - }); - - describe("connect", () => { - test("should throw error when connecting to a required relationship that is not found", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.create}(input: [{ id: "${movieId}", director: { connect: { where: { node: { id_EQ: "${directorId}" } } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director required exactly once`); - }); - - describe("nested mutations", () => { - test("should throw error when connecting to a required node that is not found", async () => { - const typeDefs = /* GraphQL */ ` - type ${Address} @node { - street: String! - } - - type ${Director} @node { - id: ID! - address: ${Address}! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.create}( - input: [ - { - id: "${movieId}" - director: { - connect: { - where: { node: { id_EQ: "${directorId}" } } - connect: { address: { where: { node: { street_EQ: "some-street" } } } } - } - } - } - ] - ) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Director} {id: "${directorId}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Director}.address required exactly once`); - }); - }); - }); - - describe("disconnect", () => { - describe("nested mutations", () => { - test("should throw error when disconnecting a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.update}(where: { id_EQ: "${movieId}" }, update: { director: { disconnect: { where: { node: { id_EQ: "${directorId}" } } } } }) { - info { - nodesCreated - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"})<-[:DIRECTED]-(:${Director} {id: "${directorId}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director required exactly once`); - }); - }); - }); - - describe("reconnect", () => { - test("should disconnect and then reconnect to a new node on a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director}! @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId1 = generate({ - charset: "alphabetic", - }); - - const directorId2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" }, - update: { - director: { - disconnect: { - where: { node: { id_EQ: "${directorId1}" } } - } - connect: { - where: { node: { id_EQ: "${directorId2}" } } - } - } - } - ) { - ${Movie.plural} { - id - director { - id - } - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"})<-[:DIRECTED]-(:${Director} {id: "${directorId1}"}) - CREATE (:${Director} {id: "${directorId2}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeUndefined(); - - const movie = (result.data as any)[Movie.operations.update][Movie.plural][0]; - - expect(movie).toEqual({ - id: movieId, - director: { - id: directorId2, - }, - }); - }); - - test("should disconnect and then reconnect to a new node on a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director} @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId1 = generate({ - charset: "alphabetic", - }); - - const directorId2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" }, - update: { - director: { - disconnect: { - where: { node: { id_EQ: "${directorId1}" } } - } - connect: { - where: { node: { id_EQ: "${directorId2}" } } - } - } - } - ) { - ${Movie.plural} { - id - director { - id - } - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"})<-[:DIRECTED]-(:${Director} {id: "${directorId1}"}) - CREATE (:${Director} {id: "${directorId2}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeUndefined(); - - const movie = (result.data as any)[Movie.operations.update][Movie.plural][0]; - - expect(movie).toEqual({ - id: movieId, - director: { - id: directorId2, - }, - }); - }); - }); - - describe("relationship length", () => { - test("should throw if connecting to more than one node", async () => { - const typeDefs = /* GraphQL */ ` - type ${Director} @node { - id: ID! - } - - type ${Movie} @node { - id: ID! - director: ${Director} @relationship(type: "DIRECTED", direction: IN) - } - `; - - await testHelper.initNeo4jGraphQL({ typeDefs }); - - const movieId = generate({ - charset: "alphabetic", - }); - - const directorId1 = generate({ - charset: "alphabetic", - }); - - const directorId2 = generate({ - charset: "alphabetic", - }); - - const mutation = ` - mutation { - ${Movie.operations.update}( - where: { id_EQ: "${movieId}" }, - update: { - director: { connect: { where: { node: { id_IN: ["${directorId1}", "${directorId2}"] } } } } - } - ) { - ${Movie.plural} { - id - director { - id - } - } - } - } - `; - - await testHelper.executeCypher(` - CREATE (:${Movie} {id: "${movieId}"}) - CREATE (:${Director} {id: "${directorId1}"}) - CREATE (:${Director} {id: "${directorId2}"}) - `); - - const result = await testHelper.executeGraphQL(mutation); - - expect(result.errors).toBeTruthy(); - expect((result.errors as any[])[0].message).toBe(`${Movie}.director must be less than or equal to one`); - }); - }); - }); -}); diff --git a/packages/graphql/tests/integration/subscriptions/auth/allow.int.test.ts b/packages/graphql/tests/integration/subscriptions/auth/allow.int.test.ts index 4ceb1b90f1..a9c34fd869 100644 --- a/packages/graphql/tests/integration/subscriptions/auth/allow.int.test.ts +++ b/packages/graphql/tests/integration/subscriptions/auth/allow.int.test.ts @@ -153,7 +153,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [[${userType.name}!]!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -212,7 +212,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -275,7 +275,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -285,7 +285,7 @@ describe("auth/allow", () => { } extend type ${postType.name} - @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -336,7 +336,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -346,7 +346,7 @@ describe("auth/allow", () => { } extend type ${postType.name} - @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -402,14 +402,14 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) - comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) + comments: [[${commentType.name}!]!]! @relationship(type: "HAS_COMMENT", direction: OUT) } type ${userType.name} @node { @@ -419,7 +419,7 @@ describe("auth/allow", () => { } extend type ${commentType.name} - @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization(validate: [{ when: [BEFORE], operations: [READ], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -579,7 +579,7 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -640,7 +640,7 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID content: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -758,10 +758,10 @@ describe("auth/allow", () => { type ${postType.name} @node { id: ID name: String - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } - extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -822,7 +822,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -830,7 +830,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -884,13 +884,13 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - post: ${postType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + post: [${postType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) - comments: ${commentType.name}! @relationship(type: "HAS_COMMENT", direction: OUT) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) + comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) } type ${userType.name} @node { @@ -898,7 +898,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [DELETE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -969,7 +969,7 @@ describe("auth/allow", () => { const typeDefs = ` type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) } type ${userType.name} @node { @@ -977,7 +977,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ @@ -1032,13 +1032,13 @@ describe("auth/allow", () => { type ${commentType.name} @node { id: ID content: String - post: ${postType.name}! @relationship(type: "HAS_COMMENT", direction: IN) + post: [${postType.name}!]! @relationship(type: "HAS_COMMENT", direction: IN) } type ${postType.name} @node { id: ID - creator: ${userType.name}! @relationship(type: "HAS_POST", direction: IN) - comments: ${commentType.name}! @relationship(type: "HAS_COMMENT", direction: OUT) + creator: [${userType.name}!]! @relationship(type: "HAS_POST", direction: IN) + comments: [${commentType.name}!]! @relationship(type: "HAS_COMMENT", direction: OUT) } type ${userType.name} @node { @@ -1046,7 +1046,7 @@ describe("auth/allow", () => { posts: [${postType.name}!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type ${postType.name} @authorization(validate: [{ when: [BEFORE], operations: [CREATE_RELATIONSHIP], where: { node: { creator_SINGLE: { id_EQ: "$jwt.sub" } } } }]) `; const userId = generate({ diff --git a/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts b/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts deleted file mode 100644 index c5b568f7fc..0000000000 --- a/packages/graphql/tests/integration/subscriptions/create/connect-or-create.int.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { DocumentNode } from "graphql"; -import { gql } from "graphql-tag"; -import type { Integer } from "neo4j-driver"; -import { TestHelper } from "../../../utils/tests-helper"; - -describe("Create -> ConnectOrCreate", () => { - const testHelper = new TestHelper({ cdc: true }); - let typeDefs: DocumentNode; - let cdcEnabled: boolean; - - const typeMovie = testHelper.createUniqueType("Movie"); - const typeActor = testHelper.createUniqueType("Actor"); - - beforeAll(async () => { - cdcEnabled = await testHelper.assertCDCEnabled(); - typeDefs = gql` - type ${typeMovie.name} @node { - title: String! - id: Int! @unique - ${typeActor.plural}: [${typeActor.name}!]! @relationship(type: "ACTED_IN", direction: IN, properties:"ActedIn") - } - - type ${typeActor.name} @node { - name: String - ${typeMovie.plural}: [${typeMovie.name}!]! @relationship(type: "ACTED_IN", direction: OUT, properties:"ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int - } - `; - }); - - beforeEach(async () => { - await testHelper.initNeo4jGraphQL({ - typeDefs, - features: { subscriptions: await testHelper.getSubscriptionEngine() }, - }); - }); - - afterEach(async () => { - await testHelper.close(); - }); - - test("ConnectOrCreate creates new node", async () => { - if (!cdcEnabled) { - console.log("CDC NOT AVAILABLE - SKIPPING"); - return; - } - const query = /* GraphQL */ ` - mutation { - ${typeActor.operations.create}( - input: [ - { - name: "Tom Hanks" - ${typeMovie.plural}: { - connectOrCreate: { - where: { node: { id_EQ: 5 } } - onCreate: { node: { title: "The Terminal", id: 5 } } - } - } - } - ] - ) { - ${typeActor.plural} { - name - } - } - } - `; - - const gqlResult = await testHelper.executeGraphQL(query); - expect(gqlResult.errors).toBeUndefined(); - expect((gqlResult as any).data[typeActor.operations.create][`${typeActor.plural}`]).toEqual([ - { - name: "Tom Hanks", - }, - ]); - - const movieTitleAndId = await testHelper.executeCypher(` - MATCH (m:${typeMovie.name} {id: 5}) - RETURN m.title as title, m.id as id - `); - - expect(movieTitleAndId.records).toHaveLength(1); - expect(movieTitleAndId.records[0]?.toObject().title).toBe("The Terminal"); - expect((movieTitleAndId.records[0]?.toObject().id as Integer).toNumber()).toBe(5); - - const actedInRelation = await testHelper.executeCypher(` - MATCH (:${typeMovie.name} {id: 5})<-[r:ACTED_IN]-(:${typeActor.name} {name: "Tom Hanks"}) - RETURN r.screentime as screentime - `); - - expect(actedInRelation.records).toHaveLength(1); - }); -}); diff --git a/packages/graphql/tests/integration/types/bigint.int.test.ts b/packages/graphql/tests/integration/types/bigint.int.test.ts index 159c5a9ab4..b7d63b661e 100644 --- a/packages/graphql/tests/integration/types/bigint.int.test.ts +++ b/packages/graphql/tests/integration/types/bigint.int.test.ts @@ -185,7 +185,7 @@ describe("BigInt", () => { const query = ` query { - ${File.plural}(where: { name_EQ: "${name}" }) { + ${File.plural}(where: { name: {eq: "${name}" } }) { name size } diff --git a/packages/graphql/tests/integration/types/date.int.test.ts b/packages/graphql/tests/integration/types/date.int.test.ts index 48f357ae64..cd4d3b541a 100644 --- a/packages/graphql/tests/integration/types/date.int.test.ts +++ b/packages/graphql/tests/integration/types/date.int.test.ts @@ -82,7 +82,7 @@ describe("Date", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID - dates: [Date] + dates: [Date!] } `; @@ -98,8 +98,8 @@ describe("Date", () => { const create = /* GraphQL */ ` mutation { ${Movie.operations.create}(input: [{ id: "${id}", dates: ["${ - date.toISOString().split("T")[0] - }", "${date.toISOString()}", "${date.toISOString()}"] }]) { + date.toISOString().split("T")[0] + }", "${date.toISOString()}", "${date.toISOString()}"] }]) { ${Movie.plural} { dates } @@ -143,7 +143,7 @@ describe("Date", () => { const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { date_EQ: "${date.toISOString()}" }) { + ${Movie.plural}(where: { date: { eq: "${date.toISOString()}" }}) { date } } @@ -189,7 +189,7 @@ describe("Date", () => { mutation { ${ Movie.operations.update - }(where: { id_EQ: "${id}"}, update: { date_SET: "${date.toISOString()}" }) { + }(where: { id_EQ: "${id}"}, update: { date: {set: "${date.toISOString()}" } }) { ${Movie.plural} { id date diff --git a/packages/graphql/tests/integration/types/datetime.int.test.ts b/packages/graphql/tests/integration/types/datetime.int.test.ts index fb9ef99f0d..b27554b3b5 100644 --- a/packages/graphql/tests/integration/types/datetime.int.test.ts +++ b/packages/graphql/tests/integration/types/datetime.int.test.ts @@ -83,7 +83,7 @@ describe("DateTime", () => { const typeDefs = /* GraphQL */ ` type ${Movie} @node { id: ID - datetimes: [DateTime] + datetimes: [DateTime!] } `; @@ -143,7 +143,7 @@ describe("DateTime", () => { const query = ` query { - ${Movie.plural}(where: { datetime_EQ: "${date.toISOString()}" }) { + ${Movie.plural}(where: { datetime: {eq: "${date.toISOString()}" } }) { datetime } } @@ -179,7 +179,7 @@ describe("DateTime", () => { const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { name_EQ: "${Movie.name}" }) { + ${Movie.plural}(where: { name: {eq: "${Movie.name}" }}) { datetime } } diff --git a/packages/graphql/tests/integration/types/duration.int.test.ts b/packages/graphql/tests/integration/types/duration.int.test.ts index c386887771..f395fad802 100644 --- a/packages/graphql/tests/integration/types/duration.int.test.ts +++ b/packages/graphql/tests/integration/types/duration.int.test.ts @@ -189,7 +189,7 @@ describe("Duration", () => { const mutation = /* GraphQL */ ` mutation ($id: ID!, $duration: Duration) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: { duration_SET: $duration }) { + ${Movie.operations.update}(where: { id_EQ: $id }, update: { duration: { set: $duration } }) { ${Movie.plural} { id duration @@ -255,7 +255,7 @@ describe("Duration", () => { const query = /* GraphQL */ ` query ($id: ID!, $duration: Duration!) { - ${Movie.plural}(where: { id_EQ: $id, duration_EQ: $duration }) { + ${Movie.plural}(where: { id: {eq: $id }, duration: { eq: $duration } }) { id duration } @@ -274,7 +274,7 @@ describe("Duration", () => { expect(parseDuration(graphqlMovie.duration)).toStrictEqual(parsedDuration); }); - test.each(["LT", "LTE", "GT", "GTE"])( + test.each(["lt", "lte", "gt", "gte"])( "should filter based on duration comparison, for filter: %s", async (filter) => { const typeDefs = ` @@ -342,7 +342,7 @@ describe("Duration", () => { const graphqlResult = await testHelper.executeGraphQL(query, { variableValues: { - where: { id_IN: [longId, mediumId, shortId], [`duration_${filter}`]: medium }, + where: { id: { in: [longId, mediumId, shortId] }, duration: { [filter]: medium } }, }, }); diff --git a/packages/graphql/tests/integration/types/float.int.test.ts b/packages/graphql/tests/integration/types/float.int.test.ts index 51781d7b80..3a75989360 100644 --- a/packages/graphql/tests/integration/types/float.int.test.ts +++ b/packages/graphql/tests/integration/types/float.int.test.ts @@ -225,7 +225,7 @@ describe("Float", () => { const query = /* GraphQL */ ` query { - ${Movie.plural}(where: { id_EQ: "${id}" }){ + ${Movie.plural}(where: { id: { eq: "${id}" } }){ fakeFloat } } diff --git a/packages/graphql/tests/integration/types/localdatetime.int.test.ts b/packages/graphql/tests/integration/types/localdatetime.int.test.ts index 7e9174dd3b..42204c42d4 100644 --- a/packages/graphql/tests/integration/types/localdatetime.int.test.ts +++ b/packages/graphql/tests/integration/types/localdatetime.int.test.ts @@ -224,7 +224,7 @@ describe("LocalDateTime", () => { const query = /* GraphQL */ ` query ($localDT: LocalDateTime!) { - ${Movie.plural}(where: { localDT_EQ: $localDT }) { + ${Movie.plural}(where: { localDT: { eq: $localDT } }) { id localDT } @@ -242,7 +242,7 @@ describe("LocalDateTime", () => { expect(graphqlMovie.id).toEqual(id); expect(parseLocalDateTime(graphqlMovie.localDT)).toStrictEqual(parsedLocalDateTime); }); - test.each(["LT", "LTE", "GT", "GTE"])( + test.each(["lt", "lte", "gt", "gte"])( "should filter based on localDT comparison, for filter %s", async (filter) => { const futureId = generate({ readable: false }); @@ -311,7 +311,7 @@ describe("LocalDateTime", () => { const graphqlResult = await testHelper.executeGraphQL(query, { variableValues: { - where: { id_IN: [futureId, presentId, pastId], [`localDT_${filter}`]: present }, + where: { id: { in: [futureId, presentId, pastId] }, localDT: { [filter]: present } }, }, }); @@ -321,13 +321,13 @@ describe("LocalDateTime", () => { expect(graphqlMovies).toBeDefined(); /* eslint-disable jest/no-conditional-expect */ - if (filter === "LT") { + if (filter === "lt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(pastId); expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPast); } - if (filter === "LTE") { + if (filter === "lte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(pastId); expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPast); @@ -336,13 +336,13 @@ describe("LocalDateTime", () => { expect(parseLocalDateTime(graphqlMovies[1]?.localDT)).toStrictEqual(parsedPresent); } - if (filter === "GT") { + if (filter === "gt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(futureId); expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedFuture); } - if (filter === "GTE") { + if (filter === "gte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(presentId); expect(parseLocalDateTime(graphqlMovies[0]?.localDT)).toStrictEqual(parsedPresent); diff --git a/packages/graphql/tests/integration/types/localtime.int.test.ts b/packages/graphql/tests/integration/types/localtime.int.test.ts index b89a58f17f..badbaa36e7 100644 --- a/packages/graphql/tests/integration/types/localtime.int.test.ts +++ b/packages/graphql/tests/integration/types/localtime.int.test.ts @@ -166,7 +166,7 @@ describe("LocalTime", () => { const mutation = /* GraphQL */ ` mutation ($id: ID!, $time: LocalTime) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: { time_SET: $time }) { + ${Movie.operations.update}(where: { id_EQ: $id }, update: { time: {set: $time} }) { ${Movie.plural} { id time @@ -222,7 +222,7 @@ describe("LocalTime", () => { const query = /* GraphQL */ ` query ($time: LocalTime!) { - ${Movie.plural}(where: { time_EQ: $time }) { + ${Movie.plural}(where: { time: { eq: $time } }) { id time } @@ -240,7 +240,7 @@ describe("LocalTime", () => { expect(graphqlMovie.id).toEqual(id); expect(parseLocalTime(graphqlMovie.time)).toStrictEqual(parsedTime); }); - test.each(["LT", "LTE", "GT", "GTE"])( + test.each(["lt", "lte", "gt", "gte"])( "should filter based on time comparison, for filter %s", async (filter) => { const futureId = generate({ readable: false }); @@ -300,7 +300,7 @@ describe("LocalTime", () => { const graphqlResult = await testHelper.executeGraphQL(query, { variableValues: { - where: { id_IN: [futureId, presentId, pastId], [`time_${filter}`]: present }, + where: { id: { in: [futureId, presentId, pastId] }, time: { [filter]: present } }, }, }); @@ -310,13 +310,13 @@ describe("LocalTime", () => { expect(graphqlMovies).toBeDefined(); /* eslint-disable jest/no-conditional-expect */ - if (filter === "LT") { + if (filter === "lt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(pastId); expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); } - if (filter === "LTE") { + if (filter === "lte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(pastId); expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); @@ -325,13 +325,13 @@ describe("LocalTime", () => { expect(parseLocalTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); } - if (filter === "GT") { + if (filter === "gt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(futureId); expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); } - if (filter === "GTE") { + if (filter === "gte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(presentId); expect(parseLocalTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPresent); diff --git a/packages/graphql/tests/integration/types/point-cartesian.int.test.ts b/packages/graphql/tests/integration/types/point-cartesian.int.test.ts index 5814de30ba..fb1813d30b 100644 --- a/packages/graphql/tests/integration/types/point-cartesian.int.test.ts +++ b/packages/graphql/tests/integration/types/point-cartesian.int.test.ts @@ -155,7 +155,7 @@ describe("CartesianPoint", () => { const update = /* GraphQL */ ` mutation UpdateParts($serial: String!, $x: Float!, $y: Float!) { - ${Part.operations.update}(where: { serial_EQ: $serial }, update: { location_SET: { x: $x, y: $y } }) { + ${Part.operations.update}(where: { serial: {eq: $serial } }, update: { location_SET: { x: $x, y: $y } }) { ${Part.plural} { serial location { @@ -218,7 +218,7 @@ describe("CartesianPoint", () => { const update = /* GraphQL */ ` mutation UpdateParts($serial: String!, $x: Float!, $y: Float!, $z: Float!) { - ${Part.operations.update}(where: { serial_EQ: $serial }, update: { location_SET: { x: $x, y: $y, z: $z } }) { + ${Part.operations.update}(where: { serial: { eq: $serial } }, update: { location_SET: { x: $x, y: $y, z: $z } }) { ${Part.plural} { serial location { @@ -279,7 +279,7 @@ describe("CartesianPoint", () => { const partsQuery = /* GraphQL */ ` query Parts($serial: String!) { - ${Part.plural}(where: { serial_EQ: $serial }) { + ${Part.plural}(where: { serial: {eq: $serial} }) { serial location { x @@ -330,7 +330,7 @@ describe("CartesianPoint", () => { const partsQuery = /* GraphQL */ ` query Parts($serial: String!) { - ${Part.plural}(where: { serial_EQ: $serial }) { + ${Part.plural}(where: { serial: {eq: $serial } }) { serial location { x diff --git a/packages/graphql/tests/integration/types/point.int.test.ts b/packages/graphql/tests/integration/types/point.int.test.ts index 16dec61048..f085db42f9 100644 --- a/packages/graphql/tests/integration/types/point.int.test.ts +++ b/packages/graphql/tests/integration/types/point.int.test.ts @@ -194,7 +194,7 @@ describe("Point", () => { mutation UpdatePhotographs($id: String!, $longitude: Float!, $latitude: Float!) { ${Photograph.operations.update}( where: { id_EQ: $id } - update: { location_SET: { longitude: $longitude, latitude: $latitude } } + update: { location: { set: { longitude: $longitude, latitude: $latitude } } } ) { ${Photograph.plural} { id @@ -263,7 +263,7 @@ describe("Point", () => { mutation UpdatePhotographs($id: String!, $longitude: Float!, $latitude: Float!, $height: Float!) { ${Photograph.operations.update}( where: { id_EQ: $id } - update: { location_SET: { longitude: $longitude, latitude: $latitude, height: $height } } + update: { location: { set: { longitude: $longitude, latitude: $latitude, height: $height } } } ) { ${Photograph.plural} { id @@ -331,7 +331,7 @@ describe("Point", () => { // Test equality const photographsEqualsQuery = /* GraphQL */ ` query Photographs($longitude: Float!, $latitude: Float!) { - ${Photograph.plural}(where: { location_EQ: { longitude: $longitude, latitude: $latitude } }) { + ${Photograph.plural}(where: { location: { eq: { longitude: $longitude, latitude: $latitude } } }) { id size location { @@ -363,7 +363,7 @@ describe("Point", () => { // Test IN functionality const photographsInQuery = /* GraphQL */ ` query Photographs($locations: [PointInput!]) { - ${Photograph.plural}(where: { location_IN: $locations }) { + ${Photograph.plural}(where: { location: {in: $locations } }) { id size location { @@ -404,7 +404,7 @@ describe("Point", () => { const photographsLessThanQuery = /* GraphQL */ ` query Photographs($longitude: Float!, $latitude: Float!) { ${Photograph.plural}( - where: { location_LT: { point: { longitude: $longitude, latitude: $latitude }, distance: 1000000 } } + where: { location: { distance: { from: { longitude: $longitude, latitude: $latitude }, lt: 1000000 } } } ) { id size @@ -438,7 +438,7 @@ describe("Point", () => { const photographsGreaterThanQuery = /* GraphQL */ ` query Photographs($longitude: Float!, $latitude: Float!) { ${Photograph.plural}( - where: { location_GT: { point: { longitude: $longitude, latitude: $latitude }, distance: 1 } } + where: { location: { distance: { from: { longitude: $longitude, latitude: $latitude }, gt: 1 } } } ) { id size @@ -494,7 +494,7 @@ describe("Point", () => { const photographsQuery = /* GraphQL */ ` query Photographs($longitude: Float!, $latitude: Float!, $height: Float) { - ${Photograph.plural}(where: { location_EQ: { longitude: $longitude, latitude: $latitude, height: $height } }) { + ${Photograph.plural}(where: { location: { eq: { longitude: $longitude, latitude: $latitude, height: $height } } }) { id size location { diff --git a/packages/graphql/tests/integration/types/points-cartesian.int.test.ts b/packages/graphql/tests/integration/types/points-cartesian.int.test.ts index b33157d50c..33dafc4b1e 100644 --- a/packages/graphql/tests/integration/types/points-cartesian.int.test.ts +++ b/packages/graphql/tests/integration/types/points-cartesian.int.test.ts @@ -264,7 +264,7 @@ describe("[CartesianPoint]", () => { const update = /* GraphQL */ ` mutation UpdateParts($id: String!, $locations: [CartesianPointInput!]) { - ${Part.operations.update}(where: { id_EQ: $id }, update: { locations_SET: $locations }) { + ${Part.operations.update}(where: { id: { eq: $id } }, update: { locations_SET: $locations }) { ${Part.plural} { id locations { @@ -328,7 +328,7 @@ describe("[CartesianPoint]", () => { const partsQuery = /* GraphQL */ ` query Parts($id: String!) { - ${Part.plural}(where: { id_EQ: $id }) { + ${Part.plural}(where: { id: { eq: $id } }) { id locations { y @@ -373,7 +373,7 @@ describe("[CartesianPoint]", () => { const partsQuery = /* GraphQL */ ` query Parts($id: String!) { - ${Part.plural}(where: { id_EQ: $id }) { + ${Part.plural}(where: { id: { eq: $id } }) { id locations { y diff --git a/packages/graphql/tests/integration/types/points.int.test.ts b/packages/graphql/tests/integration/types/points.int.test.ts index b95d1502ae..2705afca94 100644 --- a/packages/graphql/tests/integration/types/points.int.test.ts +++ b/packages/graphql/tests/integration/types/points.int.test.ts @@ -334,7 +334,7 @@ describe("[Point]", () => { // Test for equality const routesQuery = /* GraphQL */ ` query Routes($waypoints: [PointInput!]) { - ${Route.plural}(where: { waypoints_EQ: $waypoints }) { + ${Route.plural}(where: { waypoints: { eq: $waypoints } }) { id waypoints { latitude @@ -357,7 +357,7 @@ describe("[Point]", () => { // Test INCLUDES functionality const routesIncludesQuery = /* GraphQL */ ` query RoutesIncludes($waypoint: PointInput) { - ${Route.plural}(where: { waypoints_INCLUDES: $waypoint }) { + ${Route.plural}(where: { waypoints: {includes: $waypoint } }) { id waypoints { latitude @@ -404,7 +404,7 @@ describe("[Point]", () => { const routesQuery = /* GraphQL */ ` query Routes($id: String!) { - ${Route.plural}(where: { id_EQ: $id }) { + ${Route.plural}(where: { id: { eq: $id } }) { id waypoints { latitude diff --git a/packages/graphql/tests/integration/types/time.int.test.ts b/packages/graphql/tests/integration/types/time.int.test.ts index c7bf472332..5c48d99dfb 100644 --- a/packages/graphql/tests/integration/types/time.int.test.ts +++ b/packages/graphql/tests/integration/types/time.int.test.ts @@ -17,9 +17,8 @@ * limitations under the License. */ -import { Time, isTime } from "neo4j-driver"; +import { isTime } from "neo4j-driver"; import { generate } from "randomstring"; -import { parseTime } from "../../../src/graphql/scalars/Time"; import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; @@ -48,8 +47,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const time = "2024-01-29T03:57:32.358Z".split("T")[1]; - const parsedTime = parseTime(time); + const time = "2024-01-29T03:57:32.358000000Z".split("T")[1]; const mutation = ` mutation ($id: ID!, $time: Time!) { @@ -71,7 +69,7 @@ describe("Time", () => { ][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toBe(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); const neo4jResult = await testHelper.executeCypher( ` @@ -85,7 +83,7 @@ describe("Time", () => { expect(neo4jMovie).toBeDefined(); expect(neo4jMovie.id).toEqual(id); expect(isTime(neo4jMovie.time)).toBe(true); - expect(parseTime(neo4jMovie.time.toString())).toStrictEqual(parsedTime); + expect(neo4jMovie.time.toString()).toStrictEqual(time); }); test("should create a movie (with many Times)", async () => { @@ -99,8 +97,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const times = [...new Array(4)].map(() => "2023-06-09T11:17:47.789Z".split("T")[1]); - const parsedTimes = times.map((time) => parseTime(time)); + const times = [...new Array(4)].map(() => "2023-06-09T11:17:47.789000000Z".split("T")[1]); const mutation = ` mutation ($id: ID!, $times: [Time!]!) { @@ -123,10 +120,8 @@ describe("Time", () => { expect(graphqlMovie.id).toBe(id); expect(graphqlMovie.times).toHaveLength(times.length); - const parsedGraphQLTimes = graphqlMovie.times.map((time) => parseTime(time)); - - parsedTimes.forEach((parsedTime) => { - expect(parsedGraphQLTimes).toContainEqual(parsedTime); + times.forEach((time) => { + expect(graphqlMovie.times).toContainEqual(time); }); const neo4jResult = await testHelper.executeCypher( @@ -146,10 +141,8 @@ describe("Time", () => { expect(isTime(time)).toBe(true); } - const parsedNeo4jTimes = neo4jMovie.times.map((time) => parseTime(time.toString())); - - parsedTimes.forEach((parsedTime) => { - expect(parsedNeo4jTimes).toContainEqual(parsedTime); + neo4jMovie.times.forEach((time) => { + expect(times).toContainEqual(time.toString()); }); }); }); @@ -166,8 +159,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const time = "2023-07-12T05:44:06.918Z".split("T")[1]; - const parsedTime = parseTime(time); + const time = "2023-07-12T05:44:06.918000000Z".split("T")[1]; await testHelper.executeCypher( ` @@ -197,7 +189,7 @@ describe("Time", () => { ][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toEqual(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); const neo4jResult = await testHelper.executeCypher( ` @@ -211,7 +203,7 @@ describe("Time", () => { expect(neo4jMovie).toBeDefined(); expect(neo4jMovie.id).toEqual(id); expect(isTime(neo4jMovie.time)).toBe(true); - expect(parseTime(neo4jMovie.time.toString())).toStrictEqual(parsedTime); + expect(neo4jMovie.time.toString()).toStrictEqual(time); }); }); @@ -227,22 +219,20 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const date = new Date("2024-02-17T11:49:48.322Z"); - const time = date.toISOString().split("T")[1]; - const neo4jTime = Time.fromStandardDate(date); - const parsedTime = parseTime(time); + const time = "11:49:48.322000000Z"; await testHelper.executeCypher( ` CREATE (movie:${Movie}) - SET movie = $movie + SET movie.id = $id + SET movie.time = time($time) `, - { movie: { id, time: neo4jTime } } + { id, time } ); const query = /* GraphQL */ ` query ($time: Time!) { - ${Movie.plural}(where: { time_EQ: $time }) { + ${Movie.plural}(where: { time: { eq: $time } }) { id time } @@ -256,10 +246,10 @@ describe("Time", () => { const graphqlMovie: { id: string; time: string } = (graphqlResult.data as any)[Movie.plural][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toEqual(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); }); - test.each(["LT", "LTE", "GT", "GTE"])( + test.each(["lt", "lte", "gt", "gte"])( "should filter based on time comparison for filter: %s", async (filter) => { const typeDefs = /* GraphQL */ ` @@ -272,51 +262,33 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const futureId = generate({ readable: false }); - const future = "13:00:00"; - const parsedFuture = parseTime(future); - const neo4jFuture = new Time( - parsedFuture.hour, - parsedFuture.minute, - parsedFuture.second, - parsedFuture.nanosecond, - parsedFuture.timeZoneOffsetSeconds - ); + const future = "13:00:00Z"; const presentId = generate({ readable: false }); - const present = "12:00:00"; - const parsedPresent = parseTime(present); - const neo4jPresent = new Time( - parsedPresent.hour, - parsedPresent.minute, - parsedPresent.second, - parsedPresent.nanosecond, - parsedPresent.timeZoneOffsetSeconds - ); + const present = "12:00:00Z"; const pastId = generate({ readable: false }); - const past = "11:00:00"; - const parsedPast = parseTime(past); - const neo4jPast = new Time( - parsedPast.hour, - parsedPast.minute, - parsedPast.second, - parsedPast.nanosecond, - parsedPast.timeZoneOffsetSeconds - ); + const past = "11:00:00Z"; await testHelper.executeCypher( ` - CREATE (future:${Movie}) - SET future = $future - CREATE (present:${Movie}) - SET present = $present - CREATE (past:${Movie}) - SET past = $past - `, + CREATE (future:${Movie}) + SET future.id = $futureId + SET future.time = time($future) + CREATE (present:${Movie}) + SET present.id = $presentId + SET present.time = time($present) + CREATE (past:${Movie}) + SET past.id = $pastId + SET past.time = time($past) + `, { - future: { id: futureId, time: neo4jFuture }, - present: { id: presentId, time: neo4jPresent }, - past: { id: pastId, time: neo4jPast }, + futureId, + future, + presentId, + present, + pastId, + past, } ); @@ -334,7 +306,7 @@ describe("Time", () => { const graphqlResult = await testHelper.executeGraphQL(query, { variableValues: { - where: { id_IN: [futureId, presentId, pastId], [`time_${filter}`]: present }, + where: { id: { in: [futureId, presentId, pastId] }, time: { [filter]: present } }, }, }); @@ -344,34 +316,34 @@ describe("Time", () => { expect(graphqlMovies).toBeDefined(); /* eslint-disable jest/no-conditional-expect */ - if (filter === "LT") { + if (filter === "lt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); } - if (filter === "LTE") { + if (filter === "lte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); } - if (filter === "GT") { + if (filter === "gt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[0]?.time).toStrictEqual(future); } - if (filter === "GTE") { + if (filter === "gte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[0]?.time).toStrictEqual(present); expect(graphqlMovies[1]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[1]?.time).toStrictEqual(future); } /* eslint-enable jest/no-conditional-expect */ } @@ -389,51 +361,33 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const futureId = generate({ readable: false }); - const future = "13:00:00"; - const parsedFuture = parseTime(future); - const neo4jFuture = new Time( - parsedFuture.hour, - parsedFuture.minute, - parsedFuture.second, - parsedFuture.nanosecond, - parsedFuture.timeZoneOffsetSeconds - ); + const future = "13:00:00Z"; const presentId = generate({ readable: false }); - const present = "12:00:00"; - const parsedPresent = parseTime(present); - const neo4jPresent = new Time( - parsedPresent.hour, - parsedPresent.minute, - parsedPresent.second, - parsedPresent.nanosecond, - parsedPresent.timeZoneOffsetSeconds - ); + const present = "12:00:00Z"; const pastId = generate({ readable: false }); - const past = "11:00:00"; - const parsedPast = parseTime(past); - const neo4jPast = new Time( - parsedPast.hour, - parsedPast.minute, - parsedPast.second, - parsedPast.nanosecond, - parsedPast.timeZoneOffsetSeconds - ); + const past = "11:00:00Z"; await testHelper.executeCypher( ` CREATE (future:${Movie}) - SET future = $future + SET future.id = $futureId + SET future.time = time($future) CREATE (present:${Movie}) - SET present = $present + SET present.id = $presentId + SET present.time = time($present) CREATE (past:${Movie}) - SET past = $past + SET past.id = $pastId + SET past.time = time($past) `, { - future: { id: futureId, time: neo4jFuture }, - present: { id: presentId, time: neo4jPresent }, - past: { id: pastId, time: neo4jPast }, + futureId, + future, + presentId, + present, + pastId, + past, } ); @@ -464,24 +418,24 @@ describe("Time", () => { /* eslint-disable jest/no-conditional-expect */ if (sort === "ASC") { expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); expect(graphqlMovies[2]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[2]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[2]?.time).toStrictEqual(future); } if (sort === "DESC") { expect(graphqlMovies[0]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[0]?.time).toStrictEqual(future); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); expect(graphqlMovies[2]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[2]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[2]?.time).toStrictEqual(past); } /* eslint-enable jest/no-conditional-expect */ }); diff --git a/packages/graphql/tests/integration/undirected-relationships.test.ts b/packages/graphql/tests/integration/undirected-relationships.test.ts deleted file mode 100644 index c07a6c8d5e..0000000000 --- a/packages/graphql/tests/integration/undirected-relationships.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { gql } from "graphql-tag"; -import { UniqueType } from "../utils/graphql-types"; -import { TestHelper } from "../utils/tests-helper"; - -describe("undirected relationships", () => { - const testHelper = new TestHelper(); - - afterEach(async () => { - await testHelper.close(); - }); - - test("query for an undirected relationship", async () => { - const userType = new UniqueType("User"); - const typeDefs = gql` - type ${userType.name} @node { - name: String! - friends: [${userType.name}!]! @relationship(type: "FRIENDS_WITH", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - ${userType.plural}(where: { name_EQ: "Ford"}) { - name - friends: friends(directed: false) { - name - } - directedFriends: friends(directed: true) { - name - } - } - } - `; - await testHelper.executeCypher(` - CREATE (a:${userType.name} {name: "Arthur"}) - CREATE (b:${userType.name} {name: "Ford"}) - CREATE (a)-[:FRIENDS_WITH]->(b) - `); - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [userType.plural]: [ - { - name: "Ford", - directedFriends: [], - // The real treasure we made along the way - friends: [ - { - name: "Arthur", - }, - ], - }, - ], - }); - }); - - test("query for an undirected relationship on single relationship", async () => { - const userType = new UniqueType("User"); - const typeDefs = gql` - type ${userType.name} @node { - name: String! - friend: ${userType.name} @relationship(type: "FRIENDS_WITH", direction: OUT) - } - `; - - await testHelper.initNeo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - ${userType.plural}(where: {name_EQ: "Ford"}) { - name - friend: friend(directed: false) { - name - } - directedFriend: friend(directed: true) { - name - } - } - } - `; - await testHelper.executeCypher(` - CREATE (a:${userType.name} {name: "Arthur"}) - CREATE (b:${userType.name} {name: "Ford"}) - CREATE (a)-[:FRIENDS_WITH]->(b) - `); - const gqlResult = await testHelper.executeGraphQL(query); - - expect(gqlResult.errors).toBeUndefined(); - expect(gqlResult.data).toEqual({ - [userType.plural]: [ - { - name: "Ford", - directedFriend: null, - friend: { - name: "Arthur", - }, - }, - ], - }); - }); -}); diff --git a/packages/graphql/tests/integration/unwind-create/unwind-create-auth.int.test.ts b/packages/graphql/tests/integration/unwind-create/unwind-create-auth.int.test.ts index dc9d566429..72d69ec3b3 100644 --- a/packages/graphql/tests/integration/unwind-create/unwind-create-auth.int.test.ts +++ b/packages/graphql/tests/integration/unwind-create/unwind-create-auth.int.test.ts @@ -145,7 +145,7 @@ describe("unwind-create field-level auth rules", () => { } type ${Post} @node { title: String - creator: ${User} @relationship(type: "HAS_POST", direction: IN) + creator: [${User}!]! @relationship(type: "HAS_POST", direction: IN) } extend type ${User} { id: ID @authorization(validate: [{ operations: [CREATE], where: { node: { id_EQ: "$jwt.sub" } } }]) diff --git a/packages/graphql/tests/integration/update.int.test.ts b/packages/graphql/tests/integration/update.int.test.ts index c70ca5a2b8..95643bfd30 100644 --- a/packages/graphql/tests/integration/update.int.test.ts +++ b/packages/graphql/tests/integration/update.int.test.ts @@ -21,7 +21,7 @@ import { generate } from "randomstring"; import type { UniqueType } from "../utils/graphql-types"; import { TestHelper } from "../utils/tests-helper"; -describe("update (deprecate implicit _SET)", () => { +describe("update", () => { const testHelper = new TestHelper(); let Movie: UniqueType; let Actor: UniqueType; @@ -65,7 +65,7 @@ describe("update (deprecate implicit _SET)", () => { const query = /* GraphQL */ ` mutation($id: ID, $name: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: { name_SET: $name }) { + ${Movie.operations.update}(where: { id: {eq: $id } }, update: { name: { set: $name }}) { ${Movie.plural} { id name @@ -107,7 +107,7 @@ describe("update (deprecate implicit _SET)", () => { const query = /* GraphQL */ ` mutation($id: ID, $name: String) { - ${Movie.operations.update}(where: { id_EQ: $id }, update: {name_SET: $name}) { + ${Movie.operations.update}(where: { id: {eq: $id } }, update: {name: {set: $name}}) { ${Movie.plural} { id name @@ -134,18 +134,19 @@ describe("update (deprecate implicit _SET)", () => { expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ [Movie.plural]: [{ id, name: updatedName }] }); }); + test("should connect through interface relationship", async () => { const typeDefs = /* GraphQL */ ` type ${Movie} implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } type ${Series} implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } @@ -156,12 +157,12 @@ describe("update (deprecate implicit _SET)", () => { type ${Person} implements Creature @node { id: ID - movies: Production! @relationship(type: "DIRECTED", direction: OUT) + movies: [Production!]! @relationship(type: "DIRECTED", direction: OUT) } interface Creature { id: ID - movies: Production! @declareRelationship + movies: [Production!]! @declareRelationship } `; @@ -171,16 +172,16 @@ describe("update (deprecate implicit _SET)", () => { const query = /* GraphQL */ ` mutation { ${Movie.operations.update}( - where: { id_EQ: "1" }, + where: { id: { eq: "1" } }, update: { director: { connect: { - where: { node: { id_EQ: "2"} }, + where: { node: { id: { eq: "2" }} }, connect: { movies: { - where: { node: { id_EQ: "3"} }, + where: { node: { id: { eq: "3" }} }, connect: { director: { - where: { node: { id_EQ: "4"} }, + where: { node: { id: { eq: "4" }} }, connect: { movies: { - where: { node: { id_EQ: "5" } } + where: { node: { id: { eq: "5" } } } } } } } } @@ -217,7 +218,7 @@ describe("update (deprecate implicit _SET)", () => { ` ); - expect(cypherResult.records).toHaveLength(1); + expect(cypherResult.records).toHaveLength(2); expect(gqlResult?.data?.[Movie.operations.update]).toEqual({ [Movie.plural]: [{ id: "1", title: "Movie1" }], @@ -256,7 +257,7 @@ describe("update (deprecate implicit _SET)", () => { ${Movie.operations.update}( where: { actorsConnection_SOME: { node: { name_EQ: $actorName } } }, update: { - id_SET: $updatedMovieId + id: { set: $updatedMovieId } } ) { ${Movie.plural} { @@ -312,7 +313,7 @@ describe("update (deprecate implicit _SET)", () => { const query = /* GraphQL */ ` mutation($id1: ID, $id2: ID, $name: String) { - ${Movie.operations.update}(where: { OR: [{ id_EQ: $id1 }, { id_EQ: $id2 }] }, update: {name_SET: $name}) { + ${Movie.operations.update}(where: { OR: [{ id_EQ: $id1 }, { id_EQ: $id2 }] }, update: {name: { set: $name }}) { ${Movie.plural} { id name @@ -380,7 +381,7 @@ describe("update (deprecate implicit _SET)", () => { update: { actors: [{ where: { node: { name_EQ: $initialName } }, - update: { node: { name_SET: $updatedName } } + update: { node: { name: { set: $updatedName } } } }] } ) { @@ -744,10 +745,10 @@ describe("update (deprecate implicit _SET)", () => { where: { node: { name_EQ: "old actor name" } } update: { node: { - name_SET: "new actor name" + name: { set: "new actor name" } movies: [{ where: { node: { title_EQ: "old movie title" } } - update: { node: { title_SET: "new movie title" } } + update: { node: { title: { set: "new movie title" } } } }] } } @@ -987,7 +988,7 @@ describe("update (deprecate implicit _SET)", () => { type ${Photo} @node { id: ID - color: ${Color} @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -1053,7 +1054,7 @@ describe("update (deprecate implicit _SET)", () => { expect(gqlResult.errors).toBeFalsy(); expect(gqlResult?.data?.[Product.operations.update]).toEqual({ - [Product.plural]: [{ id: productId, photos: [{ id: photoId, color: null }] }], + [Product.plural]: [{ id: productId, photos: [{ id: photoId, color: [] }] }], }); }); @@ -1074,7 +1075,7 @@ describe("update (deprecate implicit _SET)", () => { type ${Photo} @node { id: ID name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -1118,7 +1119,7 @@ describe("update (deprecate implicit _SET)", () => { where: { node: { name_EQ: "Green Photo", id_EQ: "${photo0Id}" } } update: { node: { - name_SET: "Light Green Photo" + name: { set: "Light Green Photo" } color: { connect: { where: { node: { name_EQ: "Light Green", id_EQ: "${photo0Color1Id}" } } } disconnect: { where: { node: { name_EQ: "Green", id_EQ: "${photo0Color0Id}" } } } @@ -1130,7 +1131,7 @@ describe("update (deprecate implicit _SET)", () => { where: { node: { name_EQ: "Yellow Photo", id_EQ: "${photo1Id}" } } update: { node: { - name_SET: "Light Yellow Photo" + name: { set: "Light Yellow Photo" } color: { connect: { where: { node: { name_EQ: "Light Yellow", id_EQ: "${photo1Color1Id}" } } } disconnect: { where: { node: { name_EQ: "Yellow", id_EQ: "${photo1Color0Id}" } } } @@ -1196,7 +1197,7 @@ describe("update (deprecate implicit _SET)", () => { expect(greenPhoto).toMatchObject({ id: photo0Id, name: "Light Green Photo", - color: { id: photo0Color1Id, name: "Light Green" }, + color: [{ id: photo0Color1Id, name: "Light Green" }], }); const yellowPhoto = photos.find((x) => x.id === photo1Id); @@ -1204,7 +1205,7 @@ describe("update (deprecate implicit _SET)", () => { expect(yellowPhoto).toMatchObject({ id: photo1Id, name: "Light Yellow Photo", - color: { id: photo1Color1Id, name: "Light Yellow" }, + color: [{ id: photo1Color1Id, name: "Light Yellow" }], }); }); @@ -1225,7 +1226,7 @@ describe("update (deprecate implicit _SET)", () => { type ${Photo} @node { id: ID name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -1296,7 +1297,7 @@ describe("update (deprecate implicit _SET)", () => { expect((gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[])[0]).toMatchObject({ id: productId, - photos: [{ id: photoId, name: "Green Photo", color: { id: colorId, name: "Green" } }], + photos: [{ id: photoId, name: "Green Photo", color: [{ id: colorId, name: "Green" }] }], }); }); @@ -1317,7 +1318,7 @@ describe("update (deprecate implicit _SET)", () => { type ${Photo} @node { id: ID name: String - color: ${Color}! @relationship(type: "OF_COLOR", direction: OUT) + color: [${Color}!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -1386,7 +1387,7 @@ describe("update (deprecate implicit _SET)", () => { expect((gqlResult?.data?.[Product.operations.update]?.[Product.plural] as any[])[0]).toMatchObject({ id: productId, - photos: [{ id: photoId, name: "Green Photo", color: { id: colorId, name: "Green" } }], + photos: [{ id: photoId, name: "Green Photo", color: [{ id: colorId, name: "Green" }] }], }); }); }); diff --git a/packages/graphql/tests/performance/graphql/delete.graphql b/packages/graphql/tests/performance/graphql/delete.graphql index 81ed99de77..bc818afb5d 100644 --- a/packages/graphql/tests/performance/graphql/delete.graphql +++ b/packages/graphql/tests/performance/graphql/delete.graphql @@ -5,7 +5,7 @@ mutation SimpleDelete { } mutation NestedDeleteInUpdate { - updateMovies(delete: { actors: { where: { node: { name_CONTAINS: "Shark" } } } }) { + updateMovies(update: { actors: { delete: { where: { node: { name_CONTAINS: "Shark" } } } } }) { movies { title } diff --git a/packages/graphql/tests/performance/graphql/issues/2871.graphql b/packages/graphql/tests/performance/graphql/issues/2871.graphql index acced8a358..dbdf3cd137 100644 --- a/packages/graphql/tests/performance/graphql/issues/2871.graphql +++ b/packages/graphql/tests/performance/graphql/issues/2871.graphql @@ -1,5 +1,5 @@ query NestedRelationshipFilter { - movies(where: { actors: { movies_SOME: { title: "The Matrix" } } }) { + movies(where: { actors_SOME: { movies_SOME: { title: "The Matrix" } } }) { title } } diff --git a/packages/graphql/tests/performance/graphql/issues/2925.graphql b/packages/graphql/tests/performance/graphql/issues/2925.graphql index 0c376f94e4..cb3f9650cb 100644 --- a/packages/graphql/tests/performance/graphql/issues/2925.graphql +++ b/packages/graphql/tests/performance/graphql/issues/2925.graphql @@ -5,7 +5,7 @@ query SingleRelationshipFilter { } query NestedSingleRelationshipFilter { - people(where: { movies: { favouriteActor: { name_IN: ["Tom Hanks"] } } }) { + people(where: { movies_SOME: { favouriteActor: { name_IN: ["Tom Hanks"] } } }) { name } } @@ -17,7 +17,7 @@ query SingleRelationshipRequiredFilter { } query NestedSingleRelationshipRequiredFilter { - personClones(where: { movies: { favouriteActor: { name_IN: ["Tom Hanks"] } } }) { + personClones(where: { movies_SOME: { favouriteActor: { name_IN: ["Tom Hanks"] } } }) { name } } diff --git a/packages/graphql/tests/performance/graphql/query.graphql b/packages/graphql/tests/performance/graphql/query.graphql index 8ab307d10f..47f206e2dd 100644 --- a/packages/graphql/tests/performance/graphql/query.graphql +++ b/packages/graphql/tests/performance/graphql/query.graphql @@ -15,7 +15,7 @@ query SimpleQueryWithRelationship { # https://github.com/neo4j/graphql/issues/187 query QueryWhere { - movies(where: { actors: { name: "Keanu Reeves" } }) { + movies(where: { actors_SOME: { name: "Keanu Reeves" } }) { released } } @@ -66,20 +66,6 @@ query Nested { } } -query NestedWithFilter { - movies { - actors(where: { born_GT: 1000 }) { - name - movies(where: { title_CONTAINS: "Shark" }) { - title - actors { - name - } - } - } - } -} - query OrFilterOnRelationships { movies( where: { @@ -108,9 +94,9 @@ query OrFilterOnRelationshipsAndNested { { actors_SOME: { born: 1997 } } { actors_SOME: { born: 1998 } } { actors_SOME: { born: 1956 } } - { directors: { movies: { title: "Matrix" } } } - { directors: { movies: { title: "foo" } } } - { directors: { movies: { title: "bar" } } } + { directors_SOME: { movies_SOME: { title: "Matrix" } } } + { directors_SOME: { movies_SOME: { title: "foo" } } } + { directors_SOME: { movies_SOME: { title: "bar" } } } ] } ) { diff --git a/packages/graphql/tests/performance/schema/schema-performance.ts b/packages/graphql/tests/performance/schema/schema-performance.ts index b49a563a57..a7efe31d92 100644 --- a/packages/graphql/tests/performance/schema/schema-performance.ts +++ b/packages/graphql/tests/performance/schema/schema-performance.ts @@ -29,7 +29,7 @@ const basicTypeDefs = ` } type Article @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { - id: ID! @id @unique @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) + id: ID! @id @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) blocks: [Block!]! @relationship(type: "HAS_BLOCK", direction: OUT, properties: "HasBlock") images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } @@ -43,16 +43,16 @@ const basicTypeDefs = ` } type TextBlock implements Block @node { - id: ID @id @unique + id: ID @id text: String } type DividerBlock implements Block @node { - id: ID @id @unique + id: ID @id } type ImageBlock implements Block @node { - id: ID @id @unique + id: ID @id images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } @@ -85,6 +85,8 @@ export async function schemaPerformance() { typeDefs, }); console.time("Schema Generation"); - await neoSchema.getSchema(); + await neoSchema.getSchema().catch((e) => { + console.error(e); + }); console.timeEnd("Schema Generation"); } diff --git a/packages/graphql/tests/performance/server/queries.js b/packages/graphql/tests/performance/server/queries.js index 0bcbbb642a..d1e91d0907 100644 --- a/packages/graphql/tests/performance/server/queries.js +++ b/packages/graphql/tests/performance/server/queries.js @@ -1,26 +1,26 @@ export const queries = { simpleQuery: `#graphql query SimpleQuery { - movies(options: { limit: 10 }) { + movies( limit: 10) { title } } `, highComplexityQueryWithLimit: `#graphql query highComplexityQueryWithLimit { - movies(options: { sort: { title: DESC }, limit: 10 }) { + movies( sort: { title: DESC }, limit: 10) { released tagline title - actors(options: { sort: { name: DESC }, limit: 2 }) { + actors( sort: { name: DESC }, limit: 2) { name - movies(options: { sort: { title: DESC }, limit: 2 }) { + movies( sort: { title: DESC }, limit: 2) { released tagline title - actors(options: {sort: {name: DESC}, limit: 2}) { + actors(sort: {name: DESC}, limit: ) { name - movies(options: { sort: { title: DESC }, limit: 2 }) { + movies( sort: { title: DESC }, limit: 2) { released tagline title diff --git a/packages/graphql/tests/schema/aggregations.test.ts b/packages/graphql/tests/schema/aggregations.test.ts index bbdd87d28a..2563238d36 100644 --- a/packages/graphql/tests/schema/aggregations.test.ts +++ b/packages/graphql/tests/schema/aggregations.test.ts @@ -59,6 +59,23 @@ describe("Aggregations", () => { sum: BigInt } + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -80,6 +97,21 @@ describe("Aggregations", () => { min: DateTime } + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -96,6 +128,21 @@ describe("Aggregations", () => { min: Duration } + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + type FloatAggregateSelection { average: Float max: Float @@ -103,9 +150,37 @@ describe("Aggregations", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -115,6 +190,23 @@ describe("Aggregations", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" scalar LocalDateTime @@ -123,6 +215,21 @@ describe("Aggregations", () => { min: LocalDateTime } + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + \\"\\"\\" A local time, represented as a time string without timezone information \\"\\"\\" @@ -133,6 +240,21 @@ describe("Aggregations", () => { min: LocalTime } + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + type Movie { createdAt: DateTime id: ID @@ -150,7 +272,6 @@ describe("Aggregations", () => { type MovieAggregateSelection { count: Int! createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") imdbRating: FloatAggregateSelection! isbn: StringAggregateSelection! screenTime: DurationAggregateSelection! @@ -181,15 +302,6 @@ describe("Aggregations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -208,116 +320,116 @@ describe("Aggregations", () => { } input MovieUpdateInput { - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - imdbRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - imdbRating_ADD: Float - imdbRating_DIVIDE: Float - imdbRating_MULTIPLY: Float - imdbRating_SET: Float - imdbRating_SUBTRACT: Float - isbn: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isbn_SET: String - screenTime: Duration @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_SET: Duration - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _SET field\\") - someBigInt_DECREMENT: BigInt - someBigInt_INCREMENT: BigInt - someBigInt_SET: BigInt - someInt: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - someInt_DECREMENT: Int - someInt_INCREMENT: Int - someInt_SET: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalDateTime_SET: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalTime_SET: LocalTime - someTime: Time @deprecated(reason: \\"Please use the explicit _SET field\\") - someTime_SET: Time - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + imdbRating: FloatScalarMutations + imdbRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { add: ... } }' instead.\\") + imdbRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { divide: ... } }' instead.\\") + imdbRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { multiply: ... } }' instead.\\") + imdbRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'imdbRating: { set: ... } }' instead.\\") + imdbRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { subtract: ... } }' instead.\\") + isbn: StringScalarMutations + isbn_SET: String @deprecated(reason: \\"Please use the generic mutation 'isbn: { set: ... } }' instead.\\") + screenTime: DurationScalarMutations + screenTime_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime] - createdAt_LT: DateTime - createdAt_LTE: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - imdbRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbRating_EQ: Float - imdbRating_GT: Float - imdbRating_GTE: Float - imdbRating_IN: [Float] - imdbRating_LT: Float - imdbRating_LTE: Float - isbn: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isbn_CONTAINS: String - isbn_ENDS_WITH: String - isbn_EQ: String - isbn_IN: [String!] - isbn_STARTS_WITH: String - screenTime: Duration @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Duration - screenTime_GT: Duration - screenTime_GTE: Duration - screenTime_IN: [Duration] - screenTime_LT: Duration - screenTime_LTE: Duration - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - someBigInt_EQ: BigInt - someBigInt_GT: BigInt - someBigInt_GTE: BigInt - someBigInt_IN: [BigInt] - someBigInt_LT: BigInt - someBigInt_LTE: BigInt - someInt: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - someInt_EQ: Int - someInt_GT: Int - someInt_GTE: Int - someInt_IN: [Int] - someInt_LT: Int - someInt_LTE: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalDateTime_EQ: LocalDateTime - someLocalDateTime_GT: LocalDateTime - someLocalDateTime_GTE: LocalDateTime - someLocalDateTime_IN: [LocalDateTime] - someLocalDateTime_LT: LocalDateTime - someLocalDateTime_LTE: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalTime_EQ: LocalTime - someLocalTime_GT: LocalTime - someLocalTime_GTE: LocalTime - someLocalTime_IN: [LocalTime] - someLocalTime_LT: LocalTime - someLocalTime_LTE: LocalTime - someTime: Time @deprecated(reason: \\"Please use the explicit _EQ version\\") - someTime_EQ: Time - someTime_GT: Time - someTime_GTE: Time - someTime_IN: [Time] - someTime_LT: Time - someTime_LTE: Time - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + imdbRating: FloatScalarFilters + imdbRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { eq: ... }\\") + imdbRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gt: ... }\\") + imdbRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gte: ... }\\") + imdbRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { in: ... }\\") + imdbRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lt: ... }\\") + imdbRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lte: ... }\\") + isbn: StringScalarFilters + isbn_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { contains: ... }\\") + isbn_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { endsWith: ... }\\") + isbn_EQ: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { eq: ... }\\") + isbn_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter isbn: { in: ... }\\") + isbn_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { startsWith: ... }\\") + screenTime: DurationScalarFilters + screenTime_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -341,7 +453,7 @@ describe("Aggregations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -359,6 +471,20 @@ describe("Aggregations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" scalar Time @@ -367,6 +493,21 @@ describe("Aggregations", () => { min: Time } + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -439,6 +580,31 @@ describe("Aggregations", () => { sum: BigInt } + \\"\\"\\"Filters for an aggregation of an BigInt field\\"\\"\\" + input BigIntScalarAggregationFilters { + average: BigIntScalarFilters + max: BigIntScalarFilters + min: BigIntScalarFilters + sum: BigIntScalarFilters + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -465,6 +631,27 @@ describe("Aggregations", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -481,6 +668,28 @@ describe("Aggregations", () => { min: Duration } + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + type FloatAggregateSelection { average: Float max: Float @@ -488,9 +697,45 @@ describe("Aggregations", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -500,6 +745,31 @@ describe("Aggregations", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + \\"\\"\\" The edge properties for the following fields: * Post.likes @@ -521,146 +791,145 @@ describe("Aggregations", () => { AND: [LikesAggregationWhereInput!] NOT: LikesAggregationWhereInput OR: [LikesAggregationWhereInput!] - someBigInt_AVERAGE_EQUAL: BigInt - someBigInt_AVERAGE_GT: BigInt - someBigInt_AVERAGE_GTE: BigInt - someBigInt_AVERAGE_LT: BigInt - someBigInt_AVERAGE_LTE: BigInt - someBigInt_MAX_EQUAL: BigInt - someBigInt_MAX_GT: BigInt - someBigInt_MAX_GTE: BigInt - someBigInt_MAX_LT: BigInt - someBigInt_MAX_LTE: BigInt - someBigInt_MIN_EQUAL: BigInt - someBigInt_MIN_GT: BigInt - someBigInt_MIN_GTE: BigInt - someBigInt_MIN_LT: BigInt - someBigInt_MIN_LTE: BigInt - someBigInt_SUM_EQUAL: BigInt - someBigInt_SUM_GT: BigInt - someBigInt_SUM_GTE: BigInt - someBigInt_SUM_LT: BigInt - someBigInt_SUM_LTE: BigInt - someDateTime_MAX_EQUAL: DateTime - someDateTime_MAX_GT: DateTime - someDateTime_MAX_GTE: DateTime - someDateTime_MAX_LT: DateTime - someDateTime_MAX_LTE: DateTime - someDateTime_MIN_EQUAL: DateTime - someDateTime_MIN_GT: DateTime - someDateTime_MIN_GTE: DateTime - someDateTime_MIN_LT: DateTime - someDateTime_MIN_LTE: DateTime - someDuration_AVERAGE_EQUAL: Duration - someDuration_AVERAGE_GT: Duration - someDuration_AVERAGE_GTE: Duration - someDuration_AVERAGE_LT: Duration - someDuration_AVERAGE_LTE: Duration - someDuration_MAX_EQUAL: Duration - someDuration_MAX_GT: Duration - someDuration_MAX_GTE: Duration - someDuration_MAX_LT: Duration - someDuration_MAX_LTE: Duration - someDuration_MIN_EQUAL: Duration - someDuration_MIN_GT: Duration - someDuration_MIN_GTE: Duration - someDuration_MIN_LT: Duration - someDuration_MIN_LTE: Duration - someFloat_AVERAGE_EQUAL: Float - someFloat_AVERAGE_GT: Float - someFloat_AVERAGE_GTE: Float - someFloat_AVERAGE_LT: Float - someFloat_AVERAGE_LTE: Float - someFloat_MAX_EQUAL: Float - someFloat_MAX_GT: Float - someFloat_MAX_GTE: Float - someFloat_MAX_LT: Float - someFloat_MAX_LTE: Float - someFloat_MIN_EQUAL: Float - someFloat_MIN_GT: Float - someFloat_MIN_GTE: Float - someFloat_MIN_LT: Float - someFloat_MIN_LTE: Float - someFloat_SUM_EQUAL: Float - someFloat_SUM_GT: Float - someFloat_SUM_GTE: Float - someFloat_SUM_LT: Float - someFloat_SUM_LTE: Float - someID_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someInt_AVERAGE_EQUAL: Float - someInt_AVERAGE_GT: Float - someInt_AVERAGE_GTE: Float - someInt_AVERAGE_LT: Float - someInt_AVERAGE_LTE: Float - someInt_MAX_EQUAL: Int - someInt_MAX_GT: Int - someInt_MAX_GTE: Int - someInt_MAX_LT: Int - someInt_MAX_LTE: Int - someInt_MIN_EQUAL: Int - someInt_MIN_GT: Int - someInt_MIN_GTE: Int - someInt_MIN_LT: Int - someInt_MIN_LTE: Int - someInt_SUM_EQUAL: Int - someInt_SUM_GT: Int - someInt_SUM_GTE: Int - someInt_SUM_LT: Int - someInt_SUM_LTE: Int - someLocalDateTime_MAX_EQUAL: LocalDateTime - someLocalDateTime_MAX_GT: LocalDateTime - someLocalDateTime_MAX_GTE: LocalDateTime - someLocalDateTime_MAX_LT: LocalDateTime - someLocalDateTime_MAX_LTE: LocalDateTime - someLocalDateTime_MIN_EQUAL: LocalDateTime - someLocalDateTime_MIN_GT: LocalDateTime - someLocalDateTime_MIN_GTE: LocalDateTime - someLocalDateTime_MIN_LT: LocalDateTime - someLocalDateTime_MIN_LTE: LocalDateTime - someLocalTime_MAX_EQUAL: LocalTime - someLocalTime_MAX_GT: LocalTime - someLocalTime_MAX_GTE: LocalTime - someLocalTime_MAX_LT: LocalTime - someLocalTime_MAX_LTE: LocalTime - someLocalTime_MIN_EQUAL: LocalTime - someLocalTime_MIN_GT: LocalTime - someLocalTime_MIN_GTE: LocalTime - someLocalTime_MIN_LT: LocalTime - someLocalTime_MIN_LTE: LocalTime - someString_AVERAGE_LENGTH_EQUAL: Float - someString_AVERAGE_LENGTH_GT: Float - someString_AVERAGE_LENGTH_GTE: Float - someString_AVERAGE_LENGTH_LT: Float - someString_AVERAGE_LENGTH_LTE: Float - someString_LONGEST_LENGTH_EQUAL: Int - someString_LONGEST_LENGTH_GT: Int - someString_LONGEST_LENGTH_GTE: Int - someString_LONGEST_LENGTH_LT: Int - someString_LONGEST_LENGTH_LTE: Int - someString_SHORTEST_LENGTH_EQUAL: Int - someString_SHORTEST_LENGTH_GT: Int - someString_SHORTEST_LENGTH_GTE: Int - someString_SHORTEST_LENGTH_LT: Int - someString_SHORTEST_LENGTH_LTE: Int - someTime_MAX_EQUAL: Time - someTime_MAX_GT: Time - someTime_MAX_GTE: Time - someTime_MAX_LT: Time - someTime_MAX_LTE: Time - someTime_MIN_EQUAL: Time - someTime_MIN_GT: Time - someTime_MIN_GTE: Time - someTime_MIN_LT: Time - someTime_MIN_LTE: Time + someBigInt: BigIntScalarAggregationFilters + someBigInt_AVERAGE_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { eq: ... } } }' instead.\\") + someBigInt_AVERAGE_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gt: ... } } }' instead.\\") + someBigInt_AVERAGE_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gte: ... } } }' instead.\\") + someBigInt_AVERAGE_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lt: ... } } }' instead.\\") + someBigInt_AVERAGE_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lte: ... } } }' instead.\\") + someBigInt_MAX_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { eq: ... } } }' instead.\\") + someBigInt_MAX_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gt: ... } } }' instead.\\") + someBigInt_MAX_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gte: ... } } }' instead.\\") + someBigInt_MAX_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lt: ... } } }' instead.\\") + someBigInt_MAX_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lte: ... } } }' instead.\\") + someBigInt_MIN_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { eq: ... } } }' instead.\\") + someBigInt_MIN_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gt: ... } } }' instead.\\") + someBigInt_MIN_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gte: ... } } }' instead.\\") + someBigInt_MIN_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lt: ... } } }' instead.\\") + someBigInt_MIN_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lte: ... } } }' instead.\\") + someBigInt_SUM_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { eq: ... } } }' instead.\\") + someBigInt_SUM_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gt: ... } } }' instead.\\") + someBigInt_SUM_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gte: ... } } }' instead.\\") + someBigInt_SUM_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lt: ... } } }' instead.\\") + someBigInt_SUM_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lte: ... } } }' instead.\\") + someDateTime: DateTimeScalarAggregationFilters + someDateTime_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { eq: ... } } }' instead.\\") + someDateTime_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gt: ... } } }' instead.\\") + someDateTime_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gte: ... } } }' instead.\\") + someDateTime_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lt: ... } } }' instead.\\") + someDateTime_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lte: ... } } }' instead.\\") + someDateTime_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { eq: ... } } }' instead.\\") + someDateTime_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gt: ... } } }' instead.\\") + someDateTime_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gte: ... } } }' instead.\\") + someDateTime_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lt: ... } } }' instead.\\") + someDateTime_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lte: ... } } }' instead.\\") + someDuration: DurationScalarAggregationFilters + someDuration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { eq: ... } } }' instead.\\") + someDuration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gt: ... } } }' instead.\\") + someDuration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gte: ... } } }' instead.\\") + someDuration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lt: ... } } }' instead.\\") + someDuration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lte: ... } } }' instead.\\") + someDuration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { eq: ... } } }' instead.\\") + someDuration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gt: ... } } }' instead.\\") + someDuration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gte: ... } } }' instead.\\") + someDuration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lt: ... } } }' instead.\\") + someDuration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lte: ... } } }' instead.\\") + someDuration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { eq: ... } } }' instead.\\") + someDuration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gt: ... } } }' instead.\\") + someDuration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gte: ... } } }' instead.\\") + someDuration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lt: ... } } }' instead.\\") + someDuration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lte: ... } } }' instead.\\") + someFloat: FloatScalarAggregationFilters + someFloat_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { eq: ... } } }' instead.\\") + someFloat_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gt: ... } } }' instead.\\") + someFloat_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gte: ... } } }' instead.\\") + someFloat_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lt: ... } } }' instead.\\") + someFloat_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lte: ... } } }' instead.\\") + someFloat_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { eq: ... } } }' instead.\\") + someFloat_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gt: ... } } }' instead.\\") + someFloat_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gte: ... } } }' instead.\\") + someFloat_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lt: ... } } }' instead.\\") + someFloat_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lte: ... } } }' instead.\\") + someFloat_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { eq: ... } } }' instead.\\") + someFloat_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gt: ... } } }' instead.\\") + someFloat_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gte: ... } } }' instead.\\") + someFloat_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lt: ... } } }' instead.\\") + someFloat_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lte: ... } } }' instead.\\") + someFloat_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { eq: ... } } }' instead.\\") + someFloat_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gt: ... } } }' instead.\\") + someFloat_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gte: ... } } }' instead.\\") + someFloat_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lt: ... } } }' instead.\\") + someFloat_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lte: ... } } }' instead.\\") + someInt: IntScalarAggregationFilters + someInt_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { eq: ... } } }' instead.\\") + someInt_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gt: ... } } }' instead.\\") + someInt_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gte: ... } } }' instead.\\") + someInt_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lt: ... } } }' instead.\\") + someInt_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lte: ... } } }' instead.\\") + someInt_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { eq: ... } } }' instead.\\") + someInt_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gt: ... } } }' instead.\\") + someInt_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gte: ... } } }' instead.\\") + someInt_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lt: ... } } }' instead.\\") + someInt_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lte: ... } } }' instead.\\") + someInt_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { eq: ... } } }' instead.\\") + someInt_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gt: ... } } }' instead.\\") + someInt_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gte: ... } } }' instead.\\") + someInt_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lt: ... } } }' instead.\\") + someInt_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lte: ... } } }' instead.\\") + someInt_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { eq: ... } } }' instead.\\") + someInt_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gt: ... } } }' instead.\\") + someInt_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gte: ... } } }' instead.\\") + someInt_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lt: ... } } }' instead.\\") + someInt_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lte: ... } } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { eq: ... } } }' instead.\\") + someLocalDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gt: ... } } }' instead.\\") + someLocalDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gte: ... } } }' instead.\\") + someLocalDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lt: ... } } }' instead.\\") + someLocalDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lte: ... } } }' instead.\\") + someLocalDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { eq: ... } } }' instead.\\") + someLocalDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gt: ... } } }' instead.\\") + someLocalDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gte: ... } } }' instead.\\") + someLocalDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lt: ... } } }' instead.\\") + someLocalDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lte: ... } } }' instead.\\") + someLocalTime: LocalTimeScalarAggregationFilters + someLocalTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { eq: ... } } }' instead.\\") + someLocalTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gt: ... } } }' instead.\\") + someLocalTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gte: ... } } }' instead.\\") + someLocalTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lt: ... } } }' instead.\\") + someLocalTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lte: ... } } }' instead.\\") + someLocalTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { eq: ... } } }' instead.\\") + someLocalTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gt: ... } } }' instead.\\") + someLocalTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gte: ... } } }' instead.\\") + someLocalTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lt: ... } } }' instead.\\") + someLocalTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lte: ... } } }' instead.\\") + someString: StringScalarAggregationFilters + someString_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { eq: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gte: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { eq: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { eq: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lte: ... } } }' instead.\\") + someTime: TimeScalarAggregationFilters + someTime_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { eq: ... } } }' instead.\\") + someTime_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gt: ... } } }' instead.\\") + someTime_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gte: ... } } }' instead.\\") + someTime_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lt: ... } } }' instead.\\") + someTime_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lte: ... } } }' instead.\\") + someTime_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { eq: ... } } }' instead.\\") + someTime_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gt: ... } } }' instead.\\") + someTime_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gte: ... } } }' instead.\\") + someTime_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lt: ... } } }' instead.\\") + someTime_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lte: ... } } }' instead.\\") } input LikesCreateInput { @@ -690,108 +959,108 @@ describe("Aggregations", () => { } input LikesUpdateInput { - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _SET field\\") - someBigInt_DECREMENT: BigInt - someBigInt_INCREMENT: BigInt - someBigInt_SET: BigInt - someDateTime: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someDateTime_SET: DateTime - someDuration: Duration @deprecated(reason: \\"Please use the explicit _SET field\\") - someDuration_SET: Duration - someFloat: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - someFloat_ADD: Float - someFloat_DIVIDE: Float - someFloat_MULTIPLY: Float - someFloat_SET: Float - someFloat_SUBTRACT: Float - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - someInt: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - someInt_DECREMENT: Int - someInt_INCREMENT: Int - someInt_SET: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalDateTime_SET: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalTime_SET: LocalTime - someString: String @deprecated(reason: \\"Please use the explicit _SET field\\") - someString_SET: String - someTime: Time @deprecated(reason: \\"Please use the explicit _SET field\\") - someTime_SET: Time + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someID: IDScalarMutations + someID_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someID: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") } input LikesWhere { AND: [LikesWhere!] NOT: LikesWhere OR: [LikesWhere!] - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - someBigInt_EQ: BigInt - someBigInt_GT: BigInt - someBigInt_GTE: BigInt - someBigInt_IN: [BigInt] - someBigInt_LT: BigInt - someBigInt_LTE: BigInt - someDateTime: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someDateTime_EQ: DateTime - someDateTime_GT: DateTime - someDateTime_GTE: DateTime - someDateTime_IN: [DateTime] - someDateTime_LT: DateTime - someDateTime_LTE: DateTime - someDuration: Duration @deprecated(reason: \\"Please use the explicit _EQ version\\") - someDuration_EQ: Duration - someDuration_GT: Duration - someDuration_GTE: Duration - someDuration_IN: [Duration] - someDuration_LT: Duration - someDuration_LTE: Duration - someFloat: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - someFloat_EQ: Float - someFloat_GT: Float - someFloat_GTE: Float - someFloat_IN: [Float] - someFloat_LT: Float - someFloat_LTE: Float - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - someInt: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - someInt_EQ: Int - someInt_GT: Int - someInt_GTE: Int - someInt_IN: [Int] - someInt_LT: Int - someInt_LTE: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalDateTime_EQ: LocalDateTime - someLocalDateTime_GT: LocalDateTime - someLocalDateTime_GTE: LocalDateTime - someLocalDateTime_IN: [LocalDateTime] - someLocalDateTime_LT: LocalDateTime - someLocalDateTime_LTE: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalTime_EQ: LocalTime - someLocalTime_GT: LocalTime - someLocalTime_GTE: LocalTime - someLocalTime_IN: [LocalTime] - someLocalTime_LT: LocalTime - someLocalTime_LTE: LocalTime - someString: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - someString_CONTAINS: String - someString_ENDS_WITH: String - someString_EQ: String - someString_IN: [String] - someString_STARTS_WITH: String - someTime: Time @deprecated(reason: \\"Please use the explicit _EQ version\\") - someTime_EQ: Time - someTime_GT: Time - someTime_GTE: Time - someTime_IN: [Time] - someTime_LT: Time - someTime_LTE: Time + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someID: IDScalarFilters + someID_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { contains: ... }\\") + someID_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { endsWith: ... }\\") + someID_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { eq: ... }\\") + someID_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someID: { in: ... }\\") + someID_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") } \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" @@ -802,6 +1071,27 @@ describe("Aggregations", () => { min: LocalDateTime } + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + \\"\\"\\" A local time, represented as a time string without timezone information \\"\\"\\" @@ -812,6 +1102,27 @@ describe("Aggregations", () => { min: LocalTime } + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + type Mutation { createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! @@ -830,16 +1141,15 @@ describe("Aggregations", () => { } type Post { - likes(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - likesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserLikesAggregationSelection - likesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! + likes(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + likesAggregate(where: UserWhere): PostUserLikesAggregationSelection + likesConnection(after: String, first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! someID: ID title: String } type PostAggregateSelection { count: Int! - someID: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -862,7 +1172,7 @@ describe("Aggregations", () => { AND: [PostLikesAggregateInput!] NOT: PostLikesAggregateInput OR: [PostLikesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -874,10 +1184,6 @@ describe("Aggregations", () => { input PostLikesConnectFieldInput { edge: LikesCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: UserConnectWhere } @@ -887,6 +1193,25 @@ describe("Aggregations", () => { totalCount: Int! } + input PostLikesConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + all: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + none: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + single: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + some: PostLikesConnectionWhere + } + input PostLikesConnectionSort { edge: LikesSort node: UserSort @@ -922,146 +1247,145 @@ describe("Aggregations", () => { AND: [PostLikesNodeAggregationWhereInput!] NOT: PostLikesNodeAggregationWhereInput OR: [PostLikesNodeAggregationWhereInput!] - someBigInt_AVERAGE_EQUAL: BigInt - someBigInt_AVERAGE_GT: BigInt - someBigInt_AVERAGE_GTE: BigInt - someBigInt_AVERAGE_LT: BigInt - someBigInt_AVERAGE_LTE: BigInt - someBigInt_MAX_EQUAL: BigInt - someBigInt_MAX_GT: BigInt - someBigInt_MAX_GTE: BigInt - someBigInt_MAX_LT: BigInt - someBigInt_MAX_LTE: BigInt - someBigInt_MIN_EQUAL: BigInt - someBigInt_MIN_GT: BigInt - someBigInt_MIN_GTE: BigInt - someBigInt_MIN_LT: BigInt - someBigInt_MIN_LTE: BigInt - someBigInt_SUM_EQUAL: BigInt - someBigInt_SUM_GT: BigInt - someBigInt_SUM_GTE: BigInt - someBigInt_SUM_LT: BigInt - someBigInt_SUM_LTE: BigInt - someDateTime_MAX_EQUAL: DateTime - someDateTime_MAX_GT: DateTime - someDateTime_MAX_GTE: DateTime - someDateTime_MAX_LT: DateTime - someDateTime_MAX_LTE: DateTime - someDateTime_MIN_EQUAL: DateTime - someDateTime_MIN_GT: DateTime - someDateTime_MIN_GTE: DateTime - someDateTime_MIN_LT: DateTime - someDateTime_MIN_LTE: DateTime - someDuration_AVERAGE_EQUAL: Duration - someDuration_AVERAGE_GT: Duration - someDuration_AVERAGE_GTE: Duration - someDuration_AVERAGE_LT: Duration - someDuration_AVERAGE_LTE: Duration - someDuration_MAX_EQUAL: Duration - someDuration_MAX_GT: Duration - someDuration_MAX_GTE: Duration - someDuration_MAX_LT: Duration - someDuration_MAX_LTE: Duration - someDuration_MIN_EQUAL: Duration - someDuration_MIN_GT: Duration - someDuration_MIN_GTE: Duration - someDuration_MIN_LT: Duration - someDuration_MIN_LTE: Duration - someFloat_AVERAGE_EQUAL: Float - someFloat_AVERAGE_GT: Float - someFloat_AVERAGE_GTE: Float - someFloat_AVERAGE_LT: Float - someFloat_AVERAGE_LTE: Float - someFloat_MAX_EQUAL: Float - someFloat_MAX_GT: Float - someFloat_MAX_GTE: Float - someFloat_MAX_LT: Float - someFloat_MAX_LTE: Float - someFloat_MIN_EQUAL: Float - someFloat_MIN_GT: Float - someFloat_MIN_GTE: Float - someFloat_MIN_LT: Float - someFloat_MIN_LTE: Float - someFloat_SUM_EQUAL: Float - someFloat_SUM_GT: Float - someFloat_SUM_GTE: Float - someFloat_SUM_LT: Float - someFloat_SUM_LTE: Float - someID_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someID_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - someInt_AVERAGE_EQUAL: Float - someInt_AVERAGE_GT: Float - someInt_AVERAGE_GTE: Float - someInt_AVERAGE_LT: Float - someInt_AVERAGE_LTE: Float - someInt_MAX_EQUAL: Int - someInt_MAX_GT: Int - someInt_MAX_GTE: Int - someInt_MAX_LT: Int - someInt_MAX_LTE: Int - someInt_MIN_EQUAL: Int - someInt_MIN_GT: Int - someInt_MIN_GTE: Int - someInt_MIN_LT: Int - someInt_MIN_LTE: Int - someInt_SUM_EQUAL: Int - someInt_SUM_GT: Int - someInt_SUM_GTE: Int - someInt_SUM_LT: Int - someInt_SUM_LTE: Int - someLocalDateTime_MAX_EQUAL: LocalDateTime - someLocalDateTime_MAX_GT: LocalDateTime - someLocalDateTime_MAX_GTE: LocalDateTime - someLocalDateTime_MAX_LT: LocalDateTime - someLocalDateTime_MAX_LTE: LocalDateTime - someLocalDateTime_MIN_EQUAL: LocalDateTime - someLocalDateTime_MIN_GT: LocalDateTime - someLocalDateTime_MIN_GTE: LocalDateTime - someLocalDateTime_MIN_LT: LocalDateTime - someLocalDateTime_MIN_LTE: LocalDateTime - someLocalTime_MAX_EQUAL: LocalTime - someLocalTime_MAX_GT: LocalTime - someLocalTime_MAX_GTE: LocalTime - someLocalTime_MAX_LT: LocalTime - someLocalTime_MAX_LTE: LocalTime - someLocalTime_MIN_EQUAL: LocalTime - someLocalTime_MIN_GT: LocalTime - someLocalTime_MIN_GTE: LocalTime - someLocalTime_MIN_LT: LocalTime - someLocalTime_MIN_LTE: LocalTime - someString_AVERAGE_LENGTH_EQUAL: Float - someString_AVERAGE_LENGTH_GT: Float - someString_AVERAGE_LENGTH_GTE: Float - someString_AVERAGE_LENGTH_LT: Float - someString_AVERAGE_LENGTH_LTE: Float - someString_LONGEST_LENGTH_EQUAL: Int - someString_LONGEST_LENGTH_GT: Int - someString_LONGEST_LENGTH_GTE: Int - someString_LONGEST_LENGTH_LT: Int - someString_LONGEST_LENGTH_LTE: Int - someString_SHORTEST_LENGTH_EQUAL: Int - someString_SHORTEST_LENGTH_GT: Int - someString_SHORTEST_LENGTH_GTE: Int - someString_SHORTEST_LENGTH_LT: Int - someString_SHORTEST_LENGTH_LTE: Int - someTime_MAX_EQUAL: Time - someTime_MAX_GT: Time - someTime_MAX_GTE: Time - someTime_MAX_LT: Time - someTime_MAX_LTE: Time - someTime_MIN_EQUAL: Time - someTime_MIN_GT: Time - someTime_MIN_GTE: Time - someTime_MIN_LT: Time - someTime_MIN_LTE: Time + someBigInt: BigIntScalarAggregationFilters + someBigInt_AVERAGE_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { eq: ... } } }' instead.\\") + someBigInt_AVERAGE_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gt: ... } } }' instead.\\") + someBigInt_AVERAGE_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gte: ... } } }' instead.\\") + someBigInt_AVERAGE_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lt: ... } } }' instead.\\") + someBigInt_AVERAGE_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lte: ... } } }' instead.\\") + someBigInt_MAX_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { eq: ... } } }' instead.\\") + someBigInt_MAX_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gt: ... } } }' instead.\\") + someBigInt_MAX_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gte: ... } } }' instead.\\") + someBigInt_MAX_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lt: ... } } }' instead.\\") + someBigInt_MAX_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lte: ... } } }' instead.\\") + someBigInt_MIN_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { eq: ... } } }' instead.\\") + someBigInt_MIN_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gt: ... } } }' instead.\\") + someBigInt_MIN_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gte: ... } } }' instead.\\") + someBigInt_MIN_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lt: ... } } }' instead.\\") + someBigInt_MIN_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lte: ... } } }' instead.\\") + someBigInt_SUM_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { eq: ... } } }' instead.\\") + someBigInt_SUM_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gt: ... } } }' instead.\\") + someBigInt_SUM_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gte: ... } } }' instead.\\") + someBigInt_SUM_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lt: ... } } }' instead.\\") + someBigInt_SUM_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lte: ... } } }' instead.\\") + someDateTime: DateTimeScalarAggregationFilters + someDateTime_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { eq: ... } } }' instead.\\") + someDateTime_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gt: ... } } }' instead.\\") + someDateTime_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gte: ... } } }' instead.\\") + someDateTime_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lt: ... } } }' instead.\\") + someDateTime_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lte: ... } } }' instead.\\") + someDateTime_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { eq: ... } } }' instead.\\") + someDateTime_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gt: ... } } }' instead.\\") + someDateTime_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gte: ... } } }' instead.\\") + someDateTime_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lt: ... } } }' instead.\\") + someDateTime_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lte: ... } } }' instead.\\") + someDuration: DurationScalarAggregationFilters + someDuration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { eq: ... } } }' instead.\\") + someDuration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gt: ... } } }' instead.\\") + someDuration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gte: ... } } }' instead.\\") + someDuration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lt: ... } } }' instead.\\") + someDuration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lte: ... } } }' instead.\\") + someDuration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { eq: ... } } }' instead.\\") + someDuration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gt: ... } } }' instead.\\") + someDuration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gte: ... } } }' instead.\\") + someDuration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lt: ... } } }' instead.\\") + someDuration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lte: ... } } }' instead.\\") + someDuration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { eq: ... } } }' instead.\\") + someDuration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gt: ... } } }' instead.\\") + someDuration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gte: ... } } }' instead.\\") + someDuration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lt: ... } } }' instead.\\") + someDuration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lte: ... } } }' instead.\\") + someFloat: FloatScalarAggregationFilters + someFloat_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { eq: ... } } }' instead.\\") + someFloat_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gt: ... } } }' instead.\\") + someFloat_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gte: ... } } }' instead.\\") + someFloat_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lt: ... } } }' instead.\\") + someFloat_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lte: ... } } }' instead.\\") + someFloat_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { eq: ... } } }' instead.\\") + someFloat_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gt: ... } } }' instead.\\") + someFloat_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gte: ... } } }' instead.\\") + someFloat_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lt: ... } } }' instead.\\") + someFloat_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lte: ... } } }' instead.\\") + someFloat_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { eq: ... } } }' instead.\\") + someFloat_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gt: ... } } }' instead.\\") + someFloat_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gte: ... } } }' instead.\\") + someFloat_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lt: ... } } }' instead.\\") + someFloat_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lte: ... } } }' instead.\\") + someFloat_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { eq: ... } } }' instead.\\") + someFloat_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gt: ... } } }' instead.\\") + someFloat_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gte: ... } } }' instead.\\") + someFloat_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lt: ... } } }' instead.\\") + someFloat_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lte: ... } } }' instead.\\") + someInt: IntScalarAggregationFilters + someInt_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { eq: ... } } }' instead.\\") + someInt_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gt: ... } } }' instead.\\") + someInt_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gte: ... } } }' instead.\\") + someInt_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lt: ... } } }' instead.\\") + someInt_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lte: ... } } }' instead.\\") + someInt_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { eq: ... } } }' instead.\\") + someInt_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gt: ... } } }' instead.\\") + someInt_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gte: ... } } }' instead.\\") + someInt_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lt: ... } } }' instead.\\") + someInt_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lte: ... } } }' instead.\\") + someInt_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { eq: ... } } }' instead.\\") + someInt_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gt: ... } } }' instead.\\") + someInt_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gte: ... } } }' instead.\\") + someInt_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lt: ... } } }' instead.\\") + someInt_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lte: ... } } }' instead.\\") + someInt_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { eq: ... } } }' instead.\\") + someInt_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gt: ... } } }' instead.\\") + someInt_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gte: ... } } }' instead.\\") + someInt_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lt: ... } } }' instead.\\") + someInt_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lte: ... } } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { eq: ... } } }' instead.\\") + someLocalDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gt: ... } } }' instead.\\") + someLocalDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gte: ... } } }' instead.\\") + someLocalDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lt: ... } } }' instead.\\") + someLocalDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lte: ... } } }' instead.\\") + someLocalDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { eq: ... } } }' instead.\\") + someLocalDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gt: ... } } }' instead.\\") + someLocalDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gte: ... } } }' instead.\\") + someLocalDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lt: ... } } }' instead.\\") + someLocalDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lte: ... } } }' instead.\\") + someLocalTime: LocalTimeScalarAggregationFilters + someLocalTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { eq: ... } } }' instead.\\") + someLocalTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gt: ... } } }' instead.\\") + someLocalTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gte: ... } } }' instead.\\") + someLocalTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lt: ... } } }' instead.\\") + someLocalTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lte: ... } } }' instead.\\") + someLocalTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { eq: ... } } }' instead.\\") + someLocalTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gt: ... } } }' instead.\\") + someLocalTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gte: ... } } }' instead.\\") + someLocalTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lt: ... } } }' instead.\\") + someLocalTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lte: ... } } }' instead.\\") + someString: StringScalarAggregationFilters + someString_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { eq: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gte: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { eq: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { eq: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lte: ... } } }' instead.\\") + someTime: TimeScalarAggregationFilters + someTime_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { eq: ... } } }' instead.\\") + someTime_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gt: ... } } }' instead.\\") + someTime_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gte: ... } } }' instead.\\") + someTime_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lt: ... } } }' instead.\\") + someTime_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lte: ... } } }' instead.\\") + someTime_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { eq: ... } } }' instead.\\") + someTime_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gt: ... } } }' instead.\\") + someTime_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gte: ... } } }' instead.\\") + someTime_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lt: ... } } }' instead.\\") + someTime_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lte: ... } } }' instead.\\") } type PostLikesRelationship { @@ -1084,15 +1408,6 @@ describe("Aggregations", () => { where: PostLikesConnectionWhere } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - \\"\\"\\" Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. \\"\\"\\" @@ -1103,10 +1418,10 @@ describe("Aggregations", () => { input PostUpdateInput { likes: [PostLikesUpdateFieldInput!] - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + someID: IDScalarMutations + someID_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someID: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type PostUserLikesAggregationSelection { @@ -1120,7 +1435,6 @@ describe("Aggregations", () => { someDateTime: DateTimeAggregateSelection! someDuration: DurationAggregateSelection! someFloat: FloatAggregateSelection! - someID: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") someInt: IntAggregateSelection! someLocalDateTime: LocalDateTimeAggregateSelection! someLocalTime: LocalTimeAggregateSelection! @@ -1133,7 +1447,6 @@ describe("Aggregations", () => { someDateTime: DateTimeAggregateSelection! someDuration: DurationAggregateSelection! someFloat: FloatAggregateSelection! - someID: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") someInt: IntAggregateSelection! someLocalDateTime: LocalDateTimeAggregateSelection! someLocalTime: LocalTimeAggregateSelection! @@ -1145,43 +1458,45 @@ describe("Aggregations", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] + likes: UserRelationshipFilters likesAggregate: PostLikesAggregateInput + likesConnection: PostLikesConnectionFilters \\"\\"\\" Return Posts where all of the related PostLikesConnections match this filter \\"\\"\\" - likesConnection_ALL: PostLikesConnectionWhere + likesConnection_ALL: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where none of the related PostLikesConnections match this filter \\"\\"\\" - likesConnection_NONE: PostLikesConnectionWhere + likesConnection_NONE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where one of the related PostLikesConnections match this filter \\"\\"\\" - likesConnection_SINGLE: PostLikesConnectionWhere + likesConnection_SINGLE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where some of the related PostLikesConnections match this filter \\"\\"\\" - likesConnection_SOME: PostLikesConnectionWhere + likesConnection_SOME: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" - likes_ALL: UserWhere + likes_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { all: ... }' instead.\\") \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" - likes_NONE: UserWhere + likes_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { none: ... }' instead.\\") \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" - likes_SINGLE: UserWhere + likes_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { single: ... }' instead.\\") \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" - likes_SOME: UserWhere - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + likes_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { some: ... }' instead.\\") + someID: IDScalarFilters + someID_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { contains: ... }\\") + someID_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { endsWith: ... }\\") + someID_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { eq: ... }\\") + someID_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someID: { in: ... }\\") + someID_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type PostsConnection { @@ -1191,10 +1506,10 @@ describe("Aggregations", () => { } type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -1212,6 +1527,27 @@ describe("Aggregations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" scalar Time @@ -1220,6 +1556,27 @@ describe("Aggregations", () => { min: Time } + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -1259,7 +1616,6 @@ describe("Aggregations", () => { someDateTime: DateTimeAggregateSelection! someDuration: DurationAggregateSelection! someFloat: FloatAggregateSelection! - someID: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") someInt: IntAggregateSelection! someLocalDateTime: LocalDateTimeAggregateSelection! someLocalTime: LocalTimeAggregateSelection! @@ -1289,13 +1645,1208 @@ describe("Aggregations", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere + } + + \\"\\"\\" + Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. + \\"\\"\\" + input UserSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someID: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input UserUpdateInput { + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someID: IDScalarMutations + someID_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someID: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input UserWhere { + AND: [UserWhere!] + NOT: UserWhere + OR: [UserWhere!] + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someID: IDScalarFilters + someID_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { contains: ... }\\") + someID_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { endsWith: ... }\\") + someID_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { eq: ... }\\") + someID_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someID: { in: ... }\\") + someID_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + } + + type UsersConnection { + edges: [UserEdge!]! + pageInfo: PageInfo! + totalCount: Int! + }" + `); + }); + + test("Where Level Aggregations with arrays", async () => { + const typeDefs = /* GraphQL */ ` + type User @node { + someID: ID + someString: String + someFloat: Float + someInt: Int + someBigInt: BigInt + someDateTime: DateTime + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + someDuration: Duration + } + + type Post @node { + title: String + likes: [User!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + } + + type Likes @relationshipProperties { + someID: [ID!]! + someString: [String!]! + someFloat: [Float!]! + someInt: [Int!]! + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someTime: [Time!]! + someDuration: [Duration!]! + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + A BigInt value up to 64 bits in size, which can be a number or a string if used inline, or a string only if used as a variable. Always returned as a string. + \\"\\"\\" + scalar BigInt + + type BigIntAggregateSelection { + average: BigInt + max: BigInt + min: BigInt + sum: BigInt + } + + \\"\\"\\"BigInt list filters\\"\\"\\" + input BigIntListFilters { + eq: [BigInt!] + includes: BigInt + } + + \\"\\"\\"Filters for an aggregation of an BigInt field\\"\\"\\" + input BigIntScalarAggregationFilters { + average: BigIntScalarFilters + max: BigIntScalarFilters + min: BigIntScalarFilters + sum: BigIntScalarFilters + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreatePostsMutationResponse { + info: CreateInfo! + posts: [Post!]! + } + + type CreateUsersMutationResponse { + info: CreateInfo! + users: [User!]! + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"DateTime list filters\\"\\"\\" + input DateTimeListFilters { + eq: [DateTime!] + includes: DateTime + } + + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Duration list filters\\"\\"\\" + input DurationListFilters { + eq: [Duration!] + includes: Duration + } + + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID list filters\\"\\"\\" + input IDListFilters { + eq: [ID!] + includes: ID + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\" + The edge properties for the following fields: + * Post.likes + \\"\\"\\" + type Likes { + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someDuration: [Duration!]! + someFloat: [Float!]! + someID: [ID!]! + someInt: [Int!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someString: [String!]! + someTime: [Time!]! + } + + input LikesCreateInput { + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someDuration: [Duration!]! + someFloat: [Float!]! + someID: [ID!]! + someInt: [Int!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someString: [String!]! + someTime: [Time!]! + } + + input LikesSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someID: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input LikesUpdateInput { + someBigInt: ListBigIntMutations + someBigInt_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { pop: ... } }' instead.\\") + someBigInt_PUSH: [BigInt!] @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { push: ... } }' instead.\\") + someBigInt_SET: [BigInt!] @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: ListDateTimeMutations + someDateTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { pop: ... } }' instead.\\") + someDateTime_PUSH: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { push: ... } }' instead.\\") + someDateTime_SET: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: ListDurationMutations + someDuration_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someDuration: { pop: ... } }' instead.\\") + someDuration_PUSH: [Duration!] @deprecated(reason: \\"Please use the generic mutation 'someDuration: { push: ... } }' instead.\\") + someDuration_SET: [Duration!] @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: ListFloatMutations + someFloat_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someFloat: { pop: ... } }' instead.\\") + someFloat_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'someFloat: { push: ... } }' instead.\\") + someFloat_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someID: ListIDMutations + someID_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someID: { pop: ... } }' instead.\\") + someID_PUSH: [ID!] @deprecated(reason: \\"Please use the generic mutation 'someID: { push: ... } }' instead.\\") + someID_SET: [ID!] @deprecated(reason: \\"Please use the generic mutation 'someID: { set: ... } }' instead.\\") + someInt: ListIntMutations + someInt_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { pop: ... } }' instead.\\") + someInt_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someInt: { push: ... } }' instead.\\") + someInt_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: ListLocalDateTimeMutations + someLocalDateTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { pop: ... } }' instead.\\") + someLocalDateTime_PUSH: [LocalDateTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { push: ... } }' instead.\\") + someLocalDateTime_SET: [LocalDateTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: ListLocalTimeMutations + someLocalTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { pop: ... } }' instead.\\") + someLocalTime_PUSH: [LocalTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { push: ... } }' instead.\\") + someLocalTime_SET: [LocalTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: ListStringMutations + someString_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someString: { pop: ... } }' instead.\\") + someString_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'someString: { push: ... } }' instead.\\") + someString_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: ListTimeMutations + someTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someTime: { pop: ... } }' instead.\\") + someTime_PUSH: [Time!] @deprecated(reason: \\"Please use the generic mutation 'someTime: { push: ... } }' instead.\\") + someTime_SET: [Time!] @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input LikesWhere { + AND: [LikesWhere!] + NOT: LikesWhere + OR: [LikesWhere!] + someBigInt: BigIntListFilters + someBigInt_EQ: [BigInt!] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_INCLUDES: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { includes: ... }\\") + someDateTime: DateTimeListFilters + someDateTime_EQ: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_INCLUDES: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { includes: ... }\\") + someDuration: DurationListFilters + someDuration_EQ: [Duration!] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_INCLUDES: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { includes: ... }\\") + someFloat: FloatListFilters + someFloat_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { includes: ... }\\") + someID: IDListFilters + someID_EQ: [ID!] @deprecated(reason: \\"Please use the relevant generic filter someID: { eq: ... }\\") + someID_INCLUDES: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { includes: ... }\\") + someInt: IntListFilters + someInt_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { includes: ... }\\") + someLocalDateTime: LocalDateTimeListFilters + someLocalDateTime_EQ: [LocalDateTime!] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_INCLUDES: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { includes: ... }\\") + someLocalTime: LocalTimeListFilters + someLocalTime_EQ: [LocalTime!] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_INCLUDES: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { includes: ... }\\") + someString: StringListFilters + someString_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter someString: { includes: ... }\\") + someTime: TimeListFilters + someTime_EQ: [Time!] @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_INCLUDES: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { includes: ... }\\") + } + + \\"\\"\\"Mutations for a list for BigInt\\"\\"\\" + input ListBigIntMutations { + pop: Int + push: [BigInt!] + set: [BigInt!] + } + + \\"\\"\\"Mutations for a list for DateTime\\"\\"\\" + input ListDateTimeMutations { + pop: Int + push: [DateTime!] + set: [DateTime!] + } + + \\"\\"\\"Mutations for a list for Duration\\"\\"\\" + input ListDurationMutations { + pop: Int + push: [Duration!] + set: [Duration!] + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + \\"\\"\\"Mutations for a list for ID\\"\\"\\" + input ListIDMutations { + pop: Int + push: [ID!] + set: [ID!] + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] + } + + \\"\\"\\"Mutations for a list for LocalDateTime\\"\\"\\" + input ListLocalDateTimeMutations { + pop: Int + push: [LocalDateTime!] + set: [LocalDateTime!] + } + + \\"\\"\\"Mutations for a list for LocalTime\\"\\"\\" + input ListLocalTimeMutations { + pop: Int + push: [LocalTime!] + set: [LocalTime!] + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + + \\"\\"\\"Mutations for a list for Time\\"\\"\\" + input ListTimeMutations { + pop: Int + push: [Time!] + set: [Time!] + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"LocalDateTime list filters\\"\\"\\" + input LocalDateTimeListFilters { + eq: [LocalDateTime!] + includes: LocalDateTime + } + + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"LocalTime list filters\\"\\"\\" + input LocalTimeListFilters { + eq: [LocalTime!] + includes: LocalTime + } + + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Mutation { + createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! + createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! + deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! + deleteUsers(where: UserWhere): DeleteInfo! + updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! + updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Post { + likes(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + likesAggregate(where: UserWhere): PostUserLikesAggregationSelection + likesConnection(after: String, first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! + title: String + } + + type PostAggregateSelection { + count: Int! + title: StringAggregateSelection! + } + + input PostCreateInput { + likes: PostLikesFieldInput + title: String + } + + input PostDeleteInput { + likes: [PostLikesDeleteFieldInput!] + } + + type PostEdge { + cursor: String! + node: Post! + } + + input PostLikesAggregateInput { + AND: [PostLikesAggregateInput!] + NOT: PostLikesAggregateInput + OR: [PostLikesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + node: PostLikesNodeAggregationWhereInput + } + + input PostLikesConnectFieldInput { + edge: LikesCreateInput! + where: UserConnectWhere + } + + type PostLikesConnection { + edges: [PostLikesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input PostLikesConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + all: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + none: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + single: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + some: PostLikesConnectionWhere + } + + input PostLikesConnectionSort { + edge: LikesSort + node: UserSort + } + + input PostLikesConnectionWhere { + AND: [PostLikesConnectionWhere!] + NOT: PostLikesConnectionWhere + OR: [PostLikesConnectionWhere!] + edge: LikesWhere + node: UserWhere + } + + input PostLikesCreateFieldInput { + edge: LikesCreateInput! + node: UserCreateInput! + } + + input PostLikesDeleteFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesDisconnectFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + } + + input PostLikesNodeAggregationWhereInput { + AND: [PostLikesNodeAggregationWhereInput!] + NOT: PostLikesNodeAggregationWhereInput + OR: [PostLikesNodeAggregationWhereInput!] + someBigInt: BigIntScalarAggregationFilters + someBigInt_AVERAGE_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { eq: ... } } }' instead.\\") + someBigInt_AVERAGE_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gt: ... } } }' instead.\\") + someBigInt_AVERAGE_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { gte: ... } } }' instead.\\") + someBigInt_AVERAGE_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lt: ... } } }' instead.\\") + someBigInt_AVERAGE_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { average: { lte: ... } } }' instead.\\") + someBigInt_MAX_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { eq: ... } } }' instead.\\") + someBigInt_MAX_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gt: ... } } }' instead.\\") + someBigInt_MAX_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { gte: ... } } }' instead.\\") + someBigInt_MAX_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lt: ... } } }' instead.\\") + someBigInt_MAX_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { max: { lte: ... } } }' instead.\\") + someBigInt_MIN_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { eq: ... } } }' instead.\\") + someBigInt_MIN_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gt: ... } } }' instead.\\") + someBigInt_MIN_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { gte: ... } } }' instead.\\") + someBigInt_MIN_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lt: ... } } }' instead.\\") + someBigInt_MIN_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { min: { lte: ... } } }' instead.\\") + someBigInt_SUM_EQUAL: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { eq: ... } } }' instead.\\") + someBigInt_SUM_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gt: ... } } }' instead.\\") + someBigInt_SUM_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { gte: ... } } }' instead.\\") + someBigInt_SUM_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lt: ... } } }' instead.\\") + someBigInt_SUM_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter 'someBigInt: { sum: { lte: ... } } }' instead.\\") + someDateTime: DateTimeScalarAggregationFilters + someDateTime_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { eq: ... } } }' instead.\\") + someDateTime_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gt: ... } } }' instead.\\") + someDateTime_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { gte: ... } } }' instead.\\") + someDateTime_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lt: ... } } }' instead.\\") + someDateTime_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { max: { lte: ... } } }' instead.\\") + someDateTime_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { eq: ... } } }' instead.\\") + someDateTime_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gt: ... } } }' instead.\\") + someDateTime_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { gte: ... } } }' instead.\\") + someDateTime_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lt: ... } } }' instead.\\") + someDateTime_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'someDateTime: { min: { lte: ... } } }' instead.\\") + someDuration: DurationScalarAggregationFilters + someDuration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { eq: ... } } }' instead.\\") + someDuration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gt: ... } } }' instead.\\") + someDuration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { gte: ... } } }' instead.\\") + someDuration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lt: ... } } }' instead.\\") + someDuration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { average: { lte: ... } } }' instead.\\") + someDuration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { eq: ... } } }' instead.\\") + someDuration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gt: ... } } }' instead.\\") + someDuration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { gte: ... } } }' instead.\\") + someDuration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lt: ... } } }' instead.\\") + someDuration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { max: { lte: ... } } }' instead.\\") + someDuration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { eq: ... } } }' instead.\\") + someDuration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gt: ... } } }' instead.\\") + someDuration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { gte: ... } } }' instead.\\") + someDuration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lt: ... } } }' instead.\\") + someDuration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'someDuration: { min: { lte: ... } } }' instead.\\") + someFloat: FloatScalarAggregationFilters + someFloat_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { eq: ... } } }' instead.\\") + someFloat_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gt: ... } } }' instead.\\") + someFloat_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { gte: ... } } }' instead.\\") + someFloat_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lt: ... } } }' instead.\\") + someFloat_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { average: { lte: ... } } }' instead.\\") + someFloat_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { eq: ... } } }' instead.\\") + someFloat_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gt: ... } } }' instead.\\") + someFloat_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { gte: ... } } }' instead.\\") + someFloat_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lt: ... } } }' instead.\\") + someFloat_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { max: { lte: ... } } }' instead.\\") + someFloat_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { eq: ... } } }' instead.\\") + someFloat_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gt: ... } } }' instead.\\") + someFloat_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { gte: ... } } }' instead.\\") + someFloat_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lt: ... } } }' instead.\\") + someFloat_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { min: { lte: ... } } }' instead.\\") + someFloat_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { eq: ... } } }' instead.\\") + someFloat_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gt: ... } } }' instead.\\") + someFloat_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { gte: ... } } }' instead.\\") + someFloat_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lt: ... } } }' instead.\\") + someFloat_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someFloat: { sum: { lte: ... } } }' instead.\\") + someInt: IntScalarAggregationFilters + someInt_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { eq: ... } } }' instead.\\") + someInt_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gt: ... } } }' instead.\\") + someInt_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { gte: ... } } }' instead.\\") + someInt_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lt: ... } } }' instead.\\") + someInt_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { average: { lte: ... } } }' instead.\\") + someInt_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { eq: ... } } }' instead.\\") + someInt_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gt: ... } } }' instead.\\") + someInt_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { gte: ... } } }' instead.\\") + someInt_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lt: ... } } }' instead.\\") + someInt_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { max: { lte: ... } } }' instead.\\") + someInt_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { eq: ... } } }' instead.\\") + someInt_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gt: ... } } }' instead.\\") + someInt_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { gte: ... } } }' instead.\\") + someInt_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lt: ... } } }' instead.\\") + someInt_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { min: { lte: ... } } }' instead.\\") + someInt_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { eq: ... } } }' instead.\\") + someInt_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gt: ... } } }' instead.\\") + someInt_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { gte: ... } } }' instead.\\") + someInt_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lt: ... } } }' instead.\\") + someInt_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someInt: { sum: { lte: ... } } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { eq: ... } } }' instead.\\") + someLocalDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gt: ... } } }' instead.\\") + someLocalDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { gte: ... } } }' instead.\\") + someLocalDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lt: ... } } }' instead.\\") + someLocalDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { max: { lte: ... } } }' instead.\\") + someLocalDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { eq: ... } } }' instead.\\") + someLocalDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gt: ... } } }' instead.\\") + someLocalDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { gte: ... } } }' instead.\\") + someLocalDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lt: ... } } }' instead.\\") + someLocalDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalDateTime: { min: { lte: ... } } }' instead.\\") + someLocalTime: LocalTimeScalarAggregationFilters + someLocalTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { eq: ... } } }' instead.\\") + someLocalTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gt: ... } } }' instead.\\") + someLocalTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { gte: ... } } }' instead.\\") + someLocalTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lt: ... } } }' instead.\\") + someLocalTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { max: { lte: ... } } }' instead.\\") + someLocalTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { eq: ... } } }' instead.\\") + someLocalTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gt: ... } } }' instead.\\") + someLocalTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { gte: ... } } }' instead.\\") + someLocalTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lt: ... } } }' instead.\\") + someLocalTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'someLocalTime: { min: { lte: ... } } }' instead.\\") + someString: StringScalarAggregationFilters + someString_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { eq: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { gte: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lt: ... } } }' instead.\\") + someString_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'someString: { averageLength: { lte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { eq: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { gte: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lt: ... } } }' instead.\\") + someString_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { longestLength: { lte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { eq: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { gte: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lt: ... } } }' instead.\\") + someString_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'someString: { shortestLength: { lte: ... } } }' instead.\\") + someTime: TimeScalarAggregationFilters + someTime_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { eq: ... } } }' instead.\\") + someTime_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gt: ... } } }' instead.\\") + someTime_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { gte: ... } } }' instead.\\") + someTime_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lt: ... } } }' instead.\\") + someTime_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { max: { lte: ... } } }' instead.\\") + someTime_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { eq: ... } } }' instead.\\") + someTime_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gt: ... } } }' instead.\\") + someTime_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { gte: ... } } }' instead.\\") + someTime_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lt: ... } } }' instead.\\") + someTime_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'someTime: { min: { lte: ... } } }' instead.\\") + } + + type PostLikesRelationship { + cursor: String! + node: User! + properties: Likes! + } + + input PostLikesUpdateConnectionInput { + edge: LikesUpdateInput + node: UserUpdateInput + } + + input PostLikesUpdateFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + delete: [PostLikesDeleteFieldInput!] + disconnect: [PostLikesDisconnectFieldInput!] + update: PostLikesUpdateConnectionInput + where: PostLikesConnectionWhere + } + + \\"\\"\\" + Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. + \\"\\"\\" + input PostSort { + title: SortDirection + } + + input PostUpdateInput { + likes: [PostLikesUpdateFieldInput!] + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + type PostUserLikesAggregationSelection { + count: Int! + node: PostUserLikesNodeAggregateSelection + } + + type PostUserLikesNodeAggregateSelection { + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input PostWhere { + AND: [PostWhere!] + NOT: PostWhere + OR: [PostWhere!] + likes: UserRelationshipFilters + likesAggregate: PostLikesAggregateInput + likesConnection: PostLikesConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_ALL: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_NONE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SINGLE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SOME: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + likes_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + likes_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + likes_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + likes_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type PostsConnection { + edges: [PostEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postsAggregate(where: PostWhere): PostAggregateSelection! + postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + usersAggregate(where: UserWhere): UserAggregateSelection! + usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Time list filters\\"\\"\\" + input TimeListFilters { + eq: [Time!] + includes: Time + } + + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdatePostsMutationResponse { + info: UpdateInfo! + posts: [Post!]! + } + + type UpdateUsersMutationResponse { + info: UpdateInfo! + users: [User!]! + } + + type User { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someID: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserAggregateSelection { + count: Int! + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input UserConnectWhere { + node: UserWhere! + } + + input UserCreateInput { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someID: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserEdge { + cursor: String! + node: User! + } + + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere } \\"\\"\\" @@ -1315,108 +2866,108 @@ describe("Aggregations", () => { } input UserUpdateInput { - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _SET field\\") - someBigInt_DECREMENT: BigInt - someBigInt_INCREMENT: BigInt - someBigInt_SET: BigInt - someDateTime: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someDateTime_SET: DateTime - someDuration: Duration @deprecated(reason: \\"Please use the explicit _SET field\\") - someDuration_SET: Duration - someFloat: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - someFloat_ADD: Float - someFloat_DIVIDE: Float - someFloat_MULTIPLY: Float - someFloat_SET: Float - someFloat_SUBTRACT: Float - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - someInt: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - someInt_DECREMENT: Int - someInt_INCREMENT: Int - someInt_SET: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalDateTime_SET: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _SET field\\") - someLocalTime_SET: LocalTime - someString: String @deprecated(reason: \\"Please use the explicit _SET field\\") - someString_SET: String - someTime: Time @deprecated(reason: \\"Please use the explicit _SET field\\") - someTime_SET: Time + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someID: IDScalarMutations + someID_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someID: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - someBigInt: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - someBigInt_EQ: BigInt - someBigInt_GT: BigInt - someBigInt_GTE: BigInt - someBigInt_IN: [BigInt] - someBigInt_LT: BigInt - someBigInt_LTE: BigInt - someDateTime: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someDateTime_EQ: DateTime - someDateTime_GT: DateTime - someDateTime_GTE: DateTime - someDateTime_IN: [DateTime] - someDateTime_LT: DateTime - someDateTime_LTE: DateTime - someDuration: Duration @deprecated(reason: \\"Please use the explicit _EQ version\\") - someDuration_EQ: Duration - someDuration_GT: Duration - someDuration_GTE: Duration - someDuration_IN: [Duration] - someDuration_LT: Duration - someDuration_LTE: Duration - someFloat: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - someFloat_EQ: Float - someFloat_GT: Float - someFloat_GTE: Float - someFloat_IN: [Float] - someFloat_LT: Float - someFloat_LTE: Float - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - someInt: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - someInt_EQ: Int - someInt_GT: Int - someInt_GTE: Int - someInt_IN: [Int] - someInt_LT: Int - someInt_LTE: Int - someLocalDateTime: LocalDateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalDateTime_EQ: LocalDateTime - someLocalDateTime_GT: LocalDateTime - someLocalDateTime_GTE: LocalDateTime - someLocalDateTime_IN: [LocalDateTime] - someLocalDateTime_LT: LocalDateTime - someLocalDateTime_LTE: LocalDateTime - someLocalTime: LocalTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - someLocalTime_EQ: LocalTime - someLocalTime_GT: LocalTime - someLocalTime_GTE: LocalTime - someLocalTime_IN: [LocalTime] - someLocalTime_LT: LocalTime - someLocalTime_LTE: LocalTime - someString: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - someString_CONTAINS: String - someString_ENDS_WITH: String - someString_EQ: String - someString_IN: [String] - someString_STARTS_WITH: String - someTime: Time @deprecated(reason: \\"Please use the explicit _EQ version\\") - someTime_EQ: Time - someTime_GT: Time - someTime_GTE: Time - someTime_IN: [Time] - someTime_LT: Time - someTime_LTE: Time + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someID: IDScalarFilters + someID_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { contains: ... }\\") + someID_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { endsWith: ... }\\") + someID_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { eq: ... }\\") + someID_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someID: { in: ... }\\") + someID_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someID: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/array-methods.test.ts b/packages/graphql/tests/schema/array-methods.test.ts index e161f35c93..5001c8da76 100644 --- a/packages/graphql/tests/schema/array-methods.test.ts +++ b/packages/graphql/tests/schema/array-methods.test.ts @@ -38,7 +38,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; const neoSchema = new Neo4jGraphQL({ typeDefs }); @@ -56,11 +56,11 @@ describe("Arrays Methods", () => { * Movie.actors \\"\\"\\" type ActedIn { - pay: [Float] + pay: [Float!] } input ActedInCreateInput { - pay: [Float] + pay: [Float!] } input ActedInSort { @@ -68,25 +68,25 @@ describe("Arrays Methods", () => { } input ActedInUpdateInput { - pay: [Float] @deprecated(reason: \\"Please use the explicit _SET field\\") - pay_POP: Int - pay_PUSH: [Float] - pay_SET: [Float] + pay: ListFloatMutations + pay_POP: Int @deprecated(reason: \\"Please use the generic mutation 'pay: { pop: ... } }' instead.\\") + pay_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { push: ... } }' instead.\\") + pay_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - pay: [Float] @deprecated(reason: \\"Please use the explicit _EQ version\\") - pay_EQ: [Float] - pay_INCLUDES: Float + pay: FloatListFilters + pay_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter pay: { eq: ... }\\") + pay_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { includes: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String } @@ -94,7 +94,7 @@ describe("Arrays Methods", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -106,10 +106,6 @@ describe("Arrays Methods", () => { input ActorActedInConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -119,6 +115,25 @@ describe("Arrays Methods", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: MovieSort @@ -156,36 +171,27 @@ describe("Arrays Methods", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -246,16 +252,17 @@ describe("Arrays Methods", () => { type ActorMovieActedInNodeAggregateSelection { averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -267,45 +274,47 @@ describe("Arrays Methods", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -347,15 +356,74 @@ describe("Arrays Methods", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float! id: ID! ratings: [Float!]! @@ -374,7 +442,7 @@ describe("Arrays Methods", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -386,10 +454,6 @@ describe("Arrays Methods", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -399,6 +463,25 @@ describe("Arrays Methods", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -436,21 +519,22 @@ describe("Arrays Methods", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -476,7 +560,6 @@ describe("Arrays Methods", () => { type MovieAggregateSelection { averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -507,13 +590,15 @@ describe("Arrays Methods", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -526,65 +611,67 @@ describe("Arrays Methods", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _SET field\\") - ratings_POP: Int - ratings_PUSH: [Float!] - ratings_SET: [Float!] + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + ratings: ListFloatMutations + ratings_POP: Int @deprecated(reason: \\"Please use the generic mutation 'ratings: { pop: ... } }' instead.\\") + ratings_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { push: ... } }' instead.\\") + ratings_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float!] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - ratings_EQ: [Float!] - ratings_INCLUDES: Float + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + ratings: FloatListFilters + ratings_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter ratings: { eq: ... }\\") + ratings_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter ratings: { includes: ... }\\") } type MoviesConnection { @@ -611,10 +698,10 @@ describe("Arrays Methods", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -632,6 +719,27 @@ describe("Arrays Methods", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/arrays.test.ts b/packages/graphql/tests/schema/arrays.test.ts index 8b80ec9628..1eea14bbdc 100644 --- a/packages/graphql/tests/schema/arrays.test.ts +++ b/packages/graphql/tests/schema/arrays.test.ts @@ -68,9 +68,50 @@ describe("Arrays", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] } type Movie { @@ -82,7 +123,6 @@ describe("Arrays", () => { type MovieAggregateSelection { averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -96,15 +136,6 @@ describe("Arrays", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -114,40 +145,40 @@ describe("Arrays", () => { } input MovieUpdateInput { - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _SET field\\") - ratings_POP: Int - ratings_PUSH: [Float!] - ratings_SET: [Float!] + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + ratings: ListFloatMutations + ratings_POP: Int @deprecated(reason: \\"Please use the generic mutation 'ratings: { pop: ... } }' instead.\\") + ratings_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { push: ... } }' instead.\\") + ratings_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float!] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - ratings_EQ: [Float!] - ratings_INCLUDES: Float + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + ratings: FloatListFilters + ratings_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter ratings: { eq: ... }\\") + ratings_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter ratings: { includes: ... }\\") } type MoviesConnection { @@ -171,7 +202,7 @@ describe("Arrays", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/authorization.test.ts b/packages/graphql/tests/schema/authorization.test.ts index 9d13211209..3e8944e582 100644 --- a/packages/graphql/tests/schema/authorization.test.ts +++ b/packages/graphql/tests/schema/authorization.test.ts @@ -25,16 +25,16 @@ import { Neo4jGraphQL } from "../../src"; describe("Authorization", () => { test("Authorization", async () => { const typeDefs = gql` - type User @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! posts: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } - type Post @authorization(filter: [{ where: { node: { id: "$jwt.sub" } } }]) @node { + type Post @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @node { id: ID! name: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: IN) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: IN) } `; @@ -73,9 +73,38 @@ describe("Authorization", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Mutation { @@ -96,16 +125,15 @@ describe("Authorization", () => { } type Post { - author(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! + author(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + authorAggregate(where: UserWhere): PostUserAuthorAggregationSelection + authorConnection(after: String, first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! id: ID! name: String! } type PostAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -113,7 +141,7 @@ describe("Authorization", () => { AND: [PostAuthorAggregateInput!] NOT: PostAuthorAggregateInput OR: [PostAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -123,11 +151,7 @@ describe("Authorization", () => { } input PostAuthorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [UserConnectInput!] where: UserConnectWhere } @@ -137,6 +161,25 @@ describe("Authorization", () => { totalCount: Int! } + input PostAuthorConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + all: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + none: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + single: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + some: PostAuthorConnectionWhere + } + input PostAuthorConnectionSort { node: UserSort } @@ -163,39 +206,30 @@ describe("Authorization", () => { } input PostAuthorFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] } input PostAuthorNodeAggregationWhereInput { AND: [PostAuthorNodeAggregationWhereInput!] NOT: PostAuthorNodeAggregationWhereInput OR: [PostAuthorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type PostAuthorRelationship { @@ -208,10 +242,10 @@ describe("Authorization", () => { } input PostAuthorUpdateFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput - delete: PostAuthorDeleteFieldInput - disconnect: PostAuthorDisconnectFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] + delete: [PostAuthorDeleteFieldInput!] + disconnect: [PostAuthorDisconnectFieldInput!] update: PostAuthorUpdateConnectionInput where: PostAuthorConnectionWhere } @@ -223,7 +257,7 @@ describe("Authorization", () => { } input PostDeleteInput { - author: PostAuthorDeleteFieldInput + author: [PostAuthorDeleteFieldInput!] } type PostEdge { @@ -231,15 +265,6 @@ describe("Authorization", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - \\"\\"\\" Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. \\"\\"\\" @@ -249,11 +274,11 @@ describe("Authorization", () => { } input PostUpdateInput { - author: PostAuthorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + author: [PostAuthorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type PostUserAuthorAggregationSelection { @@ -262,7 +287,6 @@ describe("Authorization", () => { } type PostUserAuthorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -270,21 +294,45 @@ describe("Authorization", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] - author: UserWhere + author: UserRelationshipFilters authorAggregate: PostAuthorAggregateInput - authorConnection: PostAuthorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + authorConnection: PostAuthorConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_ALL: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_NONE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SINGLE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SOME: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + author_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + author_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + author_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + author_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PostsConnection { @@ -294,10 +342,10 @@ describe("Authorization", () => { } type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -315,6 +363,27 @@ describe("Authorization", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -338,14 +407,13 @@ describe("Authorization", () => { type User { id: ID! name: String! - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): UserUserPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! + posts(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + postsAggregate(where: UserWhere): UserUserPostsAggregationSelection + postsConnection(after: String, first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! } type UserAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -376,20 +444,11 @@ describe("Authorization", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - input UserPostsAggregateInput { AND: [UserPostsAggregateInput!] NOT: UserPostsAggregateInput OR: [UserPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -400,10 +459,6 @@ describe("Authorization", () => { input UserPostsConnectFieldInput { connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: UserConnectWhere } @@ -413,6 +468,25 @@ describe("Authorization", () => { totalCount: Int! } + input UserPostsConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserPostsConnections match this filter + \\"\\"\\" + all: UserPostsConnectionWhere + \\"\\"\\" + Return Users where none of the related UserPostsConnections match this filter + \\"\\"\\" + none: UserPostsConnectionWhere + \\"\\"\\" + Return Users where one of the related UserPostsConnections match this filter + \\"\\"\\" + single: UserPostsConnectionWhere + \\"\\"\\" + Return Users where some of the related UserPostsConnections match this filter + \\"\\"\\" + some: UserPostsConnectionWhere + } + input UserPostsConnectionSort { node: UserSort } @@ -447,31 +521,22 @@ describe("Authorization", () => { AND: [UserPostsNodeAggregationWhereInput!] NOT: UserPostsNodeAggregationWhereInput OR: [UserPostsNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type UserPostsRelationship { @@ -492,6 +557,17 @@ describe("Authorization", () => { where: UserPostsConnectionWhere } + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere + } + \\"\\"\\" Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. \\"\\"\\" @@ -501,10 +577,10 @@ describe("Authorization", () => { } input UserUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") posts: [UserPostsUpdateFieldInput!] } @@ -514,7 +590,6 @@ describe("Authorization", () => { } type UserUserPostsNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -522,43 +597,45 @@ describe("Authorization", () => { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + posts: UserRelationshipFilters postsAggregate: UserPostsAggregateInput + postsConnection: UserPostsConnectionFilters \\"\\"\\" Return Users where all of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_ALL: UserPostsConnectionWhere + postsConnection_ALL: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_NONE: UserPostsConnectionWhere + postsConnection_NONE: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_SINGLE: UserPostsConnectionWhere + postsConnection_SINGLE: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_SOME: UserPostsConnectionWhere + postsConnection_SOME: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - posts_ALL: UserWhere + posts_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - posts_NONE: UserWhere + posts_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - posts_SINGLE: UserWhere + posts_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - posts_SOME: UserWhere + posts_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { some: ... }' instead.\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/comments.test.ts b/packages/graphql/tests/schema/comments.test.ts index 9efc6dfdd2..435d206d1c 100644 --- a/packages/graphql/tests/schema/comments.test.ts +++ b/packages/graphql/tests/schema/comments.test.ts @@ -65,6 +65,16 @@ describe("Comments", () => { mutation: Mutation } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -81,6 +91,17 @@ describe("Comments", () => { \\"\\"\\"A custom scalar.\\"\\"\\" scalar CustomScalar + \\"\\"\\"CustomScalar filters\\"\\"\\" + input CustomScalarScalarFilters { + eq: CustomScalar + in: [CustomScalar!] + } + + \\"\\"\\"CustomScalar filters\\"\\"\\" + input CustomScalarScalarMutations { + set: CustomScalar + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -96,6 +117,25 @@ describe("Comments", () => { sum: Float } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + \\"\\"\\"An enumeration of movie genres.\\"\\"\\" enum Genre { ACTION @@ -103,9 +143,29 @@ describe("Comments", () => { ROMANCE } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Genre filters\\"\\"\\" + input GenreEnumScalarFilters { + eq: Genre + in: [Genre!] + } + + \\"\\"\\"Genre mutations\\"\\"\\" + input GenreEnumScalarMutations { + set: Genre + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -115,6 +175,23 @@ describe("Comments", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + \\"\\"\\"A type describing a movie.\\"\\"\\" type Movie { \\"\\"\\"The number of actors who acted in the movie.\\"\\"\\" @@ -136,7 +213,6 @@ describe("Comments", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -153,15 +229,6 @@ describe("Comments", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -175,58 +242,58 @@ describe("Comments", () => { } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - customScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _SET field\\") - customScalar_SET: CustomScalar - genre: Genre @deprecated(reason: \\"Please use the explicit _SET field\\") - genre_SET: Genre - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + customScalar: CustomScalarScalarMutations + customScalar_SET: CustomScalar @deprecated(reason: \\"Please use the generic mutation 'customScalar: { set: ... } }' instead.\\") + genre: GenreEnumScalarMutations + genre_SET: Genre @deprecated(reason: \\"Please use the generic mutation 'genre: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - customScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _EQ version\\") - customScalar_EQ: CustomScalar - customScalar_IN: [CustomScalar] - genre: Genre @deprecated(reason: \\"Please use the explicit _EQ version\\") - genre_EQ: Genre - genre_IN: [Genre] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + customScalar: CustomScalarScalarFilters + customScalar_EQ: CustomScalar @deprecated(reason: \\"Please use the relevant generic filter customScalar: { eq: ... }\\") + customScalar_IN: [CustomScalar] @deprecated(reason: \\"Please use the relevant generic filter customScalar: { in: ... }\\") + genre: GenreEnumScalarFilters + genre_EQ: Genre @deprecated(reason: \\"Please use the relevant generic filter genre: { eq: ... }\\") + genre_IN: [Genre] @deprecated(reason: \\"Please use the relevant generic filter genre: { in: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -250,7 +317,7 @@ describe("Comments", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -324,13 +391,15 @@ describe("Comments", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -341,20 +410,20 @@ describe("Comments", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -389,16 +458,45 @@ describe("Comments", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { \\"\\"\\"Actors in Movie\\"\\"\\" - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -415,7 +513,7 @@ describe("Comments", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -425,10 +523,6 @@ describe("Comments", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -438,6 +532,25 @@ describe("Comments", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -470,21 +583,22 @@ describe("Comments", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -507,7 +621,6 @@ describe("Comments", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -524,15 +637,6 @@ describe("Comments", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -542,45 +646,47 @@ describe("Comments", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -607,10 +713,10 @@ describe("Comments", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -628,6 +734,27 @@ describe("Comments", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -697,26 +824,27 @@ describe("Comments", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -728,30 +856,30 @@ describe("Comments", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { \\"\\"\\"Acted in Production\\"\\"\\" - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -759,7 +887,7 @@ describe("Comments", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -780,6 +908,25 @@ describe("Comments", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -815,21 +962,22 @@ describe("Comments", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -871,15 +1019,6 @@ describe("Comments", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -903,45 +1042,47 @@ describe("Comments", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -981,6 +1122,16 @@ describe("Comments", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -988,6 +1139,31 @@ describe("Comments", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { runtime: Int! title: String! @@ -1009,15 +1185,6 @@ describe("Comments", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1027,31 +1194,31 @@ describe("Comments", () => { } input MovieUpdateInput { - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1108,13 +1275,15 @@ describe("Comments", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -1125,22 +1294,21 @@ describe("Comments", () => { } input ProductionUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -1150,16 +1318,16 @@ describe("Comments", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -1191,15 +1359,6 @@ describe("Comments", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -1209,31 +1368,31 @@ describe("Comments", () => { } input SeriesUpdateInput { - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -1249,6 +1408,27 @@ describe("Comments", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1331,7 +1511,6 @@ describe("Comments", () => { type GenreAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input GenreConnectWhere { @@ -1347,15 +1526,6 @@ describe("Comments", () => { node: Genre! } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - \\"\\"\\" Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. \\"\\"\\" @@ -1364,20 +1534,20 @@ describe("Comments", () => { } input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type GenresConnection { @@ -1386,21 +1556,29 @@ describe("Comments", () => { totalCount: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { id: ID - search(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - searchConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieSearchConnectionWhere): MovieSearchConnection! + search(limit: Int, offset: Int, where: SearchWhere): [Search!]! + searchConnection(after: String, first: Int, where: MovieSearchConnectionWhere): MovieSearchConnection! searchNoDirective: Search } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -1429,15 +1607,6 @@ describe("Comments", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - input MovieSearchConnectInput { Genre: [MovieSearchGenreConnectFieldInput!] Movie: [MovieSearchMovieConnectFieldInput!] @@ -1449,6 +1618,25 @@ describe("Comments", () => { totalCount: Int! } + input MovieSearchConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieSearchConnections match this filter + \\"\\"\\" + all: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieSearchConnections match this filter + \\"\\"\\" + none: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieSearchConnections match this filter + \\"\\"\\" + single: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieSearchConnections match this filter + \\"\\"\\" + some: MovieSearchConnectionWhere + } + input MovieSearchConnectionWhere { Genre: MovieSearchGenreConnectionWhere Movie: MovieSearchMovieConnectionWhere @@ -1572,8 +1760,8 @@ describe("Comments", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") search: MovieSearchUpdateInput } @@ -1581,36 +1769,38 @@ describe("Comments", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + search: SearchRelationshipFilters + searchConnection: MovieSearchConnectionFilters \\"\\"\\" Return Movies where all of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_ALL: MovieSearchConnectionWhere + searchConnection_ALL: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_NONE: MovieSearchConnectionWhere + searchConnection_NONE: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_SINGLE: MovieSearchConnectionWhere + searchConnection_SINGLE: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_SOME: MovieSearchConnectionWhere + searchConnection_SOME: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Searches match this filter\\"\\"\\" - search_ALL: SearchWhere + search_ALL: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Searches match this filter\\"\\"\\" - search_NONE: SearchWhere + search_NONE: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Searches match this filter\\"\\"\\" - search_SINGLE: SearchWhere + search_SINGLE: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Searches match this filter\\"\\"\\" - search_SOME: SearchWhere + search_SOME: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { some: ... }' instead.\\") } type MoviesConnection { @@ -1637,23 +1827,28 @@ describe("Comments", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - searches(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + searches(limit: Int, offset: Int, where: SearchWhere): [Search!]! } union Search = Genre | Movie + input SearchRelationshipFilters { + \\"\\"\\"Filter type where all of the related Searches match this filter\\"\\"\\" + all: SearchWhere + \\"\\"\\"Filter type where none of the related Searches match this filter\\"\\"\\" + none: SearchWhere + \\"\\"\\"Filter type where one of the related Searches match this filter\\"\\"\\" + single: SearchWhere + \\"\\"\\"Filter type where some of the related Searches match this filter\\"\\"\\" + some: SearchWhere + } + input SearchWhere { Genre: GenreWhere Movie: MovieWhere diff --git a/packages/graphql/tests/schema/connect-or-create-id.test.ts b/packages/graphql/tests/schema/connect-or-create-id.test.ts deleted file mode 100644 index 34baa5f066..0000000000 --- a/packages/graphql/tests/schema/connect-or-create-id.test.ts +++ /dev/null @@ -1,1074 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("connect or create with id", () => { - test("connect or create with id", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - id: ID! @id @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - id: ID! - title: String! - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("connect or create with non-autogenerated id", async () => { - const typeDefs = gql` - type Post @node { - id: ID! @unique - content: String! - creator: User! @relationship(type: "HAS_POST", direction: IN) - createdAt: DateTime! - } - - type User @node { - id: ID! @id @unique - name: String! - posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreatePostsMutationResponse { - info: CreateInfo! - posts: [Post!]! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" - scalar DateTime - - type DateTimeAggregateSelection { - max: DateTime - min: DateTime - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Mutation { - createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Post { - content: String! - createdAt: DateTime! - creator(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - creatorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserCreatorAggregationSelection - creatorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostCreatorConnectionSort!], where: PostCreatorConnectionWhere): PostCreatorConnection! - id: ID! - } - - type PostAggregateSelection { - content: StringAggregateSelection! - count: Int! - createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input PostConnectInput { - creator: PostCreatorConnectFieldInput - } - - input PostConnectOrCreateWhere { - node: PostUniqueWhere! - } - - input PostConnectWhere { - node: PostWhere! - } - - input PostCreateInput { - content: String! - createdAt: DateTime! - creator: PostCreatorFieldInput - id: ID! - } - - input PostCreatorAggregateInput { - AND: [PostCreatorAggregateInput!] - NOT: PostCreatorAggregateInput - OR: [PostCreatorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: PostCreatorNodeAggregationWhereInput - } - - input PostCreatorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - input PostCreatorConnectOrCreateFieldInput { - onCreate: PostCreatorConnectOrCreateFieldInputOnCreate! - where: UserConnectOrCreateWhere! - } - - input PostCreatorConnectOrCreateFieldInputOnCreate { - node: UserOnCreateInput! - } - - type PostCreatorConnection { - edges: [PostCreatorRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input PostCreatorConnectionSort { - node: UserSort - } - - input PostCreatorConnectionWhere { - AND: [PostCreatorConnectionWhere!] - NOT: PostCreatorConnectionWhere - OR: [PostCreatorConnectionWhere!] - node: UserWhere - } - - input PostCreatorCreateFieldInput { - node: UserCreateInput! - } - - input PostCreatorDeleteFieldInput { - delete: UserDeleteInput - where: PostCreatorConnectionWhere - } - - input PostCreatorDisconnectFieldInput { - disconnect: UserDisconnectInput - where: PostCreatorConnectionWhere - } - - input PostCreatorFieldInput { - connect: PostCreatorConnectFieldInput - connectOrCreate: PostCreatorConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: PostCreatorCreateFieldInput - } - - input PostCreatorNodeAggregationWhereInput { - AND: [PostCreatorNodeAggregationWhereInput!] - NOT: PostCreatorNodeAggregationWhereInput - OR: [PostCreatorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type PostCreatorRelationship { - cursor: String! - node: User! - } - - input PostCreatorUpdateConnectionInput { - node: UserUpdateInput - } - - input PostCreatorUpdateFieldInput { - connect: PostCreatorConnectFieldInput - connectOrCreate: PostCreatorConnectOrCreateFieldInput - create: PostCreatorCreateFieldInput - delete: PostCreatorDeleteFieldInput - disconnect: PostCreatorDisconnectFieldInput - update: PostCreatorUpdateConnectionInput - where: PostCreatorConnectionWhere - } - - input PostDeleteInput { - creator: PostCreatorDeleteFieldInput - } - - input PostDisconnectInput { - creator: PostCreatorDisconnectFieldInput - } - - type PostEdge { - cursor: String! - node: Post! - } - - input PostOnCreateInput { - content: String! - createdAt: DateTime! - id: ID! - } - - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - - \\"\\"\\" - Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. - \\"\\"\\" - input PostSort { - content: SortDirection - createdAt: SortDirection - id: SortDirection - } - - input PostUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input PostUpdateInput { - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - creator: PostCreatorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - type PostUserCreatorAggregationSelection { - count: Int! - node: PostUserCreatorNodeAggregateSelection - } - - type PostUserCreatorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name: StringAggregateSelection! - } - - input PostWhere { - AND: [PostWhere!] - NOT: PostWhere - OR: [PostWhere!] - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String!] - content_STARTS_WITH: String - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime!] - createdAt_LT: DateTime - createdAt_LTE: DateTime - creator: UserWhere - creatorAggregate: PostCreatorAggregateInput - creatorConnection: PostCreatorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - } - - type PostsConnection { - edges: [PostEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(where: PostWhere): PostAggregateSelection! - postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdatePostsMutationResponse { - info: UpdateInfo! - posts: [Post!]! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - id: ID! - name: String! - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): UserPostPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! - } - - type UserAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name: StringAggregateSelection! - } - - input UserConnectInput { - posts: [UserPostsConnectFieldInput!] - } - - input UserConnectOrCreateWhere { - node: UserUniqueWhere! - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - name: String! - posts: UserPostsFieldInput - } - - input UserDeleteInput { - posts: [UserPostsDeleteFieldInput!] - } - - input UserDisconnectInput { - posts: [UserPostsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOnCreateInput { - name: String! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - type UserPostPostsAggregationSelection { - count: Int! - node: UserPostPostsNodeAggregateSelection - } - - type UserPostPostsNodeAggregateSelection { - content: StringAggregateSelection! - createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input UserPostsAggregateInput { - AND: [UserPostsAggregateInput!] - NOT: UserPostsAggregateInput - OR: [UserPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserPostsNodeAggregationWhereInput - } - - input UserPostsConnectFieldInput { - connect: [PostConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: PostConnectWhere - } - - input UserPostsConnectOrCreateFieldInput { - onCreate: UserPostsConnectOrCreateFieldInputOnCreate! - where: PostConnectOrCreateWhere! - } - - input UserPostsConnectOrCreateFieldInputOnCreate { - node: PostOnCreateInput! - } - - type UserPostsConnection { - edges: [UserPostsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserPostsConnectionSort { - node: PostSort - } - - input UserPostsConnectionWhere { - AND: [UserPostsConnectionWhere!] - NOT: UserPostsConnectionWhere - OR: [UserPostsConnectionWhere!] - node: PostWhere - } - - input UserPostsCreateFieldInput { - node: PostCreateInput! - } - - input UserPostsDeleteFieldInput { - delete: PostDeleteInput - where: UserPostsConnectionWhere - } - - input UserPostsDisconnectFieldInput { - disconnect: PostDisconnectInput - where: UserPostsConnectionWhere - } - - input UserPostsFieldInput { - connect: [UserPostsConnectFieldInput!] - connectOrCreate: [UserPostsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [UserPostsCreateFieldInput!] - } - - input UserPostsNodeAggregationWhereInput { - AND: [UserPostsNodeAggregationWhereInput!] - NOT: UserPostsNodeAggregationWhereInput - OR: [UserPostsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - createdAt_MAX_EQUAL: DateTime - createdAt_MAX_GT: DateTime - createdAt_MAX_GTE: DateTime - createdAt_MAX_LT: DateTime - createdAt_MAX_LTE: DateTime - createdAt_MIN_EQUAL: DateTime - createdAt_MIN_GT: DateTime - createdAt_MIN_GTE: DateTime - createdAt_MIN_LT: DateTime - createdAt_MIN_LTE: DateTime - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type UserPostsRelationship { - cursor: String! - node: Post! - } - - input UserPostsUpdateConnectionInput { - node: PostUpdateInput - } - - input UserPostsUpdateFieldInput { - connect: [UserPostsConnectFieldInput!] - connectOrCreate: [UserPostsConnectOrCreateFieldInput!] - create: [UserPostsCreateFieldInput!] - delete: [UserPostsDeleteFieldInput!] - disconnect: [UserPostsDisconnectFieldInput!] - update: UserPostsUpdateConnectionInput - where: UserPostsConnectionWhere - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - id: SortDirection - name: SortDirection - } - - input UserUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - - input UserUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - posts: [UserPostsUpdateFieldInput!] - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - postsAggregate: UserPostsAggregateInput - \\"\\"\\" - Return Users where all of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_ALL: UserPostsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_NONE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SINGLE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SOME: UserPostsConnectionWhere - \\"\\"\\"Return Users where all of the related Posts match this filter\\"\\"\\" - posts_ALL: PostWhere - \\"\\"\\"Return Users where none of the related Posts match this filter\\"\\"\\" - posts_NONE: PostWhere - \\"\\"\\"Return Users where one of the related Posts match this filter\\"\\"\\" - posts_SINGLE: PostWhere - \\"\\"\\"Return Users where some of the related Posts match this filter\\"\\"\\" - posts_SOME: PostWhere - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connect-or-create-unions.test.ts b/packages/graphql/tests/schema/connect-or-create-unions.test.ts deleted file mode 100644 index 8494cab31f..0000000000 --- a/packages/graphql/tests/schema/connect-or-create-unions.test.ts +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("Connect Or Create", () => { - test("With Unions", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Series @node { - title: String! - isan: String! @unique - } - - union Production = Movie | Series - - type ActedIn @relationshipProperties { - screenTime: Int! - } - - type Actor @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.actedIn - \\"\\"\\" - type ActedIn { - screenTime: Int! - } - - input ActedInCreateInput { - screenTime: Int! - } - - input ActedInSort { - screenTime: SortDirection - } - - input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - } - - type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! - name: String! - } - - type ActorActedInConnection { - edges: [ActorActedInRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorActedInConnectionSort { - edge: ActedInSort - } - - input ActorActedInConnectionWhere { - Movie: ActorActedInMovieConnectionWhere - Series: ActorActedInSeriesConnectionWhere - } - - input ActorActedInCreateInput { - Movie: ActorActedInMovieFieldInput - Series: ActorActedInSeriesFieldInput - } - - input ActorActedInDeleteInput { - Movie: [ActorActedInMovieDeleteFieldInput!] - Series: [ActorActedInSeriesDeleteFieldInput!] - } - - input ActorActedInMovieConnectFieldInput { - edge: ActedInCreateInput! - where: MovieConnectWhere - } - - input ActorActedInMovieConnectOrCreateFieldInput { - onCreate: ActorActedInMovieConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorActedInMovieConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - - input ActorActedInMovieConnectionWhere { - AND: [ActorActedInMovieConnectionWhere!] - NOT: ActorActedInMovieConnectionWhere - OR: [ActorActedInMovieConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorActedInMovieCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorActedInMovieDeleteFieldInput { - where: ActorActedInMovieConnectionWhere - } - - input ActorActedInMovieDisconnectFieldInput { - where: ActorActedInMovieConnectionWhere - } - - input ActorActedInMovieFieldInput { - connect: [ActorActedInMovieConnectFieldInput!] - connectOrCreate: [ActorActedInMovieConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorActedInMovieCreateFieldInput!] - } - - input ActorActedInMovieUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorActedInMovieUpdateFieldInput { - connect: [ActorActedInMovieConnectFieldInput!] - connectOrCreate: [ActorActedInMovieConnectOrCreateFieldInput!] - create: [ActorActedInMovieCreateFieldInput!] - delete: [ActorActedInMovieDeleteFieldInput!] - disconnect: [ActorActedInMovieDisconnectFieldInput!] - update: ActorActedInMovieUpdateConnectionInput - where: ActorActedInMovieConnectionWhere - } - - type ActorActedInRelationship { - cursor: String! - node: Production! - properties: ActedIn! - } - - input ActorActedInSeriesConnectFieldInput { - edge: ActedInCreateInput! - where: SeriesConnectWhere - } - - input ActorActedInSeriesConnectOrCreateFieldInput { - onCreate: ActorActedInSeriesConnectOrCreateFieldInputOnCreate! - where: SeriesConnectOrCreateWhere! - } - - input ActorActedInSeriesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: SeriesOnCreateInput! - } - - input ActorActedInSeriesConnectionWhere { - AND: [ActorActedInSeriesConnectionWhere!] - NOT: ActorActedInSeriesConnectionWhere - OR: [ActorActedInSeriesConnectionWhere!] - edge: ActedInWhere - node: SeriesWhere - } - - input ActorActedInSeriesCreateFieldInput { - edge: ActedInCreateInput! - node: SeriesCreateInput! - } - - input ActorActedInSeriesDeleteFieldInput { - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInSeriesDisconnectFieldInput { - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInSeriesFieldInput { - connect: [ActorActedInSeriesConnectFieldInput!] - connectOrCreate: [ActorActedInSeriesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorActedInSeriesCreateFieldInput!] - } - - input ActorActedInSeriesUpdateConnectionInput { - edge: ActedInUpdateInput - node: SeriesUpdateInput - } - - input ActorActedInSeriesUpdateFieldInput { - connect: [ActorActedInSeriesConnectFieldInput!] - connectOrCreate: [ActorActedInSeriesConnectOrCreateFieldInput!] - create: [ActorActedInSeriesCreateFieldInput!] - delete: [ActorActedInSeriesDeleteFieldInput!] - disconnect: [ActorActedInSeriesDisconnectFieldInput!] - update: ActorActedInSeriesUpdateConnectionInput - where: ActorActedInSeriesConnectionWhere - } - - input ActorActedInUpdateInput { - Movie: [ActorActedInMovieUpdateFieldInput!] - Series: [ActorActedInSeriesUpdateFieldInput!] - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - actedIn: ActorActedInCreateInput - name: String! - } - - input ActorDeleteInput { - actedIn: ActorActedInDeleteInput - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - actedIn: ActorActedInUpdateInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - \\"\\"\\" - Return Actors where all of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere - \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere - \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere - \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere - \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isan_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - deleteSeries(where: SeriesWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - union Production = Movie | Series - - input ProductionWhere { - Movie: MovieWhere - Series: SeriesWhere - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - type Series { - isan: String! - title: String! - } - - type SeriesAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input SeriesConnectOrCreateWhere { - node: SeriesUniqueWhere! - } - - input SeriesConnectWhere { - node: SeriesWhere! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - isan: String! - title: String! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - input SeriesOnCreateInput { - isan: String! - title: String! - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - isan: SortDirection - title: SortDirection - } - - input SeriesUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input SeriesUpdateInput { - isan: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isan_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connect-or-create.test.ts b/packages/graphql/tests/schema/connect-or-create.test.ts deleted file mode 100644 index c6ef2205ce..0000000000 --- a/packages/graphql/tests/schema/connect-or-create.test.ts +++ /dev/null @@ -1,971 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../src"; - -describe("Connect Or Create", () => { - test("Connect Or Create", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isan_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("Connect Or Create with relation properties", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - screentime: Int! - characterName: String - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - \\"\\"\\" - type ActedIn { - characterName: String - screentime: Int! - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - characterName_AVERAGE_LENGTH_EQUAL: Float - characterName_AVERAGE_LENGTH_GT: Float - characterName_AVERAGE_LENGTH_GTE: Float - characterName_AVERAGE_LENGTH_LT: Float - characterName_AVERAGE_LENGTH_LTE: Float - characterName_LONGEST_LENGTH_EQUAL: Int - characterName_LONGEST_LENGTH_GT: Int - characterName_LONGEST_LENGTH_GTE: Int - characterName_LONGEST_LENGTH_LT: Int - characterName_LONGEST_LENGTH_LTE: Int - characterName_SHORTEST_LENGTH_EQUAL: Int - characterName_SHORTEST_LENGTH_GT: Int - characterName_SHORTEST_LENGTH_GTE: Int - characterName_SHORTEST_LENGTH_LT: Int - characterName_SHORTEST_LENGTH_LTE: Int - screentime_AVERAGE_EQUAL: Float - screentime_AVERAGE_GT: Float - screentime_AVERAGE_GTE: Float - screentime_AVERAGE_LT: Float - screentime_AVERAGE_LTE: Float - screentime_MAX_EQUAL: Int - screentime_MAX_GT: Int - screentime_MAX_GTE: Int - screentime_MAX_LT: Int - screentime_MAX_LTE: Int - screentime_MIN_EQUAL: Int - screentime_MIN_GT: Int - screentime_MIN_GTE: Int - screentime_MIN_LT: Int - screentime_MIN_LTE: Int - screentime_SUM_EQUAL: Int - screentime_SUM_GT: Int - screentime_SUM_GTE: Int - screentime_SUM_LT: Int - screentime_SUM_LTE: Int - } - - input ActedInCreateInput { - characterName: String - screentime: Int! - } - - input ActedInSort { - characterName: SortDirection - screentime: SortDirection - } - - input ActedInUpdateInput { - characterName: String @deprecated(reason: \\"Please use the explicit _SET field\\") - characterName_SET: String - screentime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screentime_DECREMENT: Int - screentime_INCREMENT: Int - screentime_SET: Int - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - characterName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - characterName_CONTAINS: String - characterName_ENDS_WITH: String - characterName_EQ: String - characterName_IN: [String] - characterName_STARTS_WITH: String - screentime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screentime_EQ: Int - screentime_GT: Int - screentime_GTE: Int - screentime_IN: [Int!] - screentime_LT: Int - screentime_LTE: Int - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - characterName: StringAggregateSelection! - screentime: IntAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOnCreateInput { - isan: String! - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUniqueWhere { - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_EQ: String - } - - input MovieUpdateInput { - isan: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isan_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/connections/enums.test.ts b/packages/graphql/tests/schema/connections/enums.test.ts index abb1119ae0..d2b4c0eaca 100644 --- a/packages/graphql/tests/schema/connections/enums.test.ts +++ b/packages/graphql/tests/schema/connections/enums.test.ts @@ -71,23 +71,23 @@ describe("Enums", () => { } input ActedInUpdateInput { - roleType: RoleType @deprecated(reason: \\"Please use the explicit _SET field\\") - roleType_SET: RoleType + roleType: RoleTypeEnumScalarMutations + roleType_SET: RoleType @deprecated(reason: \\"Please use the generic mutation 'roleType: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - roleType: RoleType @deprecated(reason: \\"Please use the explicit _EQ version\\") - roleType_EQ: RoleType - roleType_IN: [RoleType!] + roleType: RoleTypeEnumScalarFilters + roleType_EQ: RoleType @deprecated(reason: \\"Please use the relevant generic filter roleType: { eq: ... }\\") + roleType_IN: [RoleType!] @deprecated(reason: \\"Please use the relevant generic filter roleType: { in: ... }\\") } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } @@ -135,7 +135,7 @@ describe("Enums", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -147,10 +147,6 @@ describe("Enums", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -160,6 +156,25 @@ describe("Enums", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -197,21 +212,22 @@ describe("Enums", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -234,13 +250,15 @@ describe("Enums", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -252,45 +270,47 @@ describe("Enums", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -325,10 +345,30 @@ describe("Enums", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -345,7 +385,7 @@ describe("Enums", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -357,10 +397,6 @@ describe("Enums", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -370,6 +406,25 @@ describe("Enums", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -407,21 +462,22 @@ describe("Enums", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -475,13 +531,15 @@ describe("Enums", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -493,45 +551,47 @@ describe("Enums", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -558,10 +618,10 @@ describe("Enums", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -571,6 +631,17 @@ describe("Enums", () => { SUPPORTING } + \\"\\"\\"RoleType filters\\"\\"\\" + input RoleTypeEnumScalarFilters { + eq: RoleType + in: [RoleType!] + } + + \\"\\"\\"RoleType mutations\\"\\"\\" + input RoleTypeEnumScalarMutations { + set: RoleType + } + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -584,6 +655,27 @@ describe("Enums", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/connections/interfaces.test.ts b/packages/graphql/tests/schema/connections/interfaces.test.ts index d959b6870a..e8ce46097d 100644 --- a/packages/graphql/tests/schema/connections/interfaces.test.ts +++ b/packages/graphql/tests/schema/connections/interfaces.test.ts @@ -27,14 +27,14 @@ describe("Connection with interfaces", () => { const typeDefs = gql` type Movie implements Production @subscription(events: []) @node { title: String! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } type Series implements Production @node { title: String! episode: Int! - id: ID @unique + id: ID director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } @@ -45,12 +45,12 @@ describe("Connection with interfaces", () => { type Person implements Creature @node { id: ID - movies: Production! @relationship(type: "DIRECTED", direction: OUT) + movies: [Production!]! @relationship(type: "DIRECTED", direction: OUT) } interface Creature { id: ID - movies: Production! @declareRelationship + movies: [Production!]! @declareRelationship } `; const neoSchema = new Neo4jGraphQL({ typeDefs }); @@ -87,17 +87,16 @@ describe("Connection with interfaces", () => { interface Creature { id: ID - movies(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! + movies(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! moviesConnection(after: String, first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! } type CreatureAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input CreatureConnectInput { - movies: CreatureMoviesConnectFieldInput + movies: [CreatureMoviesConnectFieldInput!] } input CreatureConnectWhere { @@ -109,11 +108,11 @@ describe("Connection with interfaces", () => { } input CreatureDeleteInput { - movies: CreatureMoviesDeleteFieldInput + movies: [CreatureMoviesDeleteFieldInput!] } input CreatureDisconnectInput { - movies: CreatureMoviesDisconnectFieldInput + movies: [CreatureMoviesDisconnectFieldInput!] } type CreatureEdge { @@ -129,13 +128,12 @@ describe("Connection with interfaces", () => { AND: [CreatureMoviesAggregateInput!] NOT: CreatureMoviesAggregateInput OR: [CreatureMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: CreatureMoviesNodeAggregationWhereInput } input CreatureMoviesConnectFieldInput { @@ -149,6 +147,25 @@ describe("Connection with interfaces", () => { totalCount: Int! } + input CreatureMoviesConnectionFilters { + \\"\\"\\" + Return Creatures where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + all: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + none: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + single: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + some: CreatureMoviesConnectionWhere + } + input CreatureMoviesConnectionSort { node: ProductionSort } @@ -174,22 +191,6 @@ describe("Connection with interfaces", () => { where: CreatureMoviesConnectionWhere } - input CreatureMoviesNodeAggregationWhereInput { - AND: [CreatureMoviesNodeAggregationWhereInput!] - NOT: CreatureMoviesNodeAggregationWhereInput - OR: [CreatureMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type CreatureMoviesRelationship { cursor: String! node: Production! @@ -200,21 +201,23 @@ describe("Connection with interfaces", () => { } input CreatureMoviesUpdateFieldInput { - connect: CreatureMoviesConnectFieldInput - create: CreatureMoviesCreateFieldInput - delete: CreatureMoviesDeleteFieldInput - disconnect: CreatureMoviesDisconnectFieldInput + connect: [CreatureMoviesConnectFieldInput!] + create: [CreatureMoviesCreateFieldInput!] + delete: [CreatureMoviesDeleteFieldInput!] + disconnect: [CreatureMoviesDisconnectFieldInput!] update: CreatureMoviesUpdateConnectionInput where: CreatureMoviesConnectionWhere } - input CreatureOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more CreatureSort objects to sort Creatures by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [CreatureSort!] + input CreatureRelationshipFilters { + \\"\\"\\"Filter type where all of the related Creatures match this filter\\"\\"\\" + all: CreatureWhere + \\"\\"\\"Filter type where none of the related Creatures match this filter\\"\\"\\" + none: CreatureWhere + \\"\\"\\"Filter type where one of the related Creatures match this filter\\"\\"\\" + single: CreatureWhere + \\"\\"\\"Filter type where some of the related Creatures match this filter\\"\\"\\" + some: CreatureWhere } \\"\\"\\" @@ -225,26 +228,57 @@ describe("Connection with interfaces", () => { } input CreatureUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - movies: CreatureMoviesUpdateFieldInput + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [CreatureMoviesUpdateFieldInput!] } input CreatureWhere { AND: [CreatureWhere!] NOT: CreatureWhere OR: [CreatureWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - movies: ProductionWhere + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: ProductionRelationshipFilters moviesAggregate: CreatureMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere + moviesConnection: CreatureMoviesConnectionFilters + \\"\\"\\" + Return Creatures where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where all of the related Productions match this filter + \\"\\"\\" + movies_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\" + Return Creatures where none of the related Productions match this filter + \\"\\"\\" + movies_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\" + Return Creatures where one of the related Productions match this filter + \\"\\"\\" + movies_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\" + Return Creatures where some of the related Productions match this filter + \\"\\"\\" + movies_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") typename: [CreatureImplementation!] - typename_IN: [CreatureImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type CreaturesConnection { @@ -261,9 +295,18 @@ describe("Connection with interfaces", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -273,17 +316,33 @@ describe("Connection with interfaces", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CreatureSort!], where: CreatureWhere): [Creature!]! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): MovieCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionDirectorConnectionSort!], where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! + director(limit: Int, offset: Int, sort: [CreatureSort!], where: CreatureWhere): [Creature!]! + directorAggregate(where: CreatureWhere): MovieCreatureDirectorAggregationSelection + directorConnection(after: String, first: Int, sort: [ProductionDirectorConnectionSort!], where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! id: ID title: String! } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -295,11 +354,6 @@ describe("Connection with interfaces", () => { type MovieCreatureDirectorAggregationSelection { count: Int! - node: MovieCreatureDirectorNodeAggregateSelection - } - - type MovieCreatureDirectorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieDeleteInput { @@ -310,13 +364,12 @@ describe("Connection with interfaces", () => { AND: [MovieDirectorAggregateInput!] NOT: MovieDirectorAggregateInput OR: [MovieDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: MovieDirectorNodeAggregationWhereInput } input MovieDirectorConnectFieldInput { @@ -324,6 +377,25 @@ describe("Connection with interfaces", () => { where: CreatureConnectWhere } + input MovieDirectorConnectionFilters { + \\"\\"\\" + Return Movies where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input MovieDirectorCreateFieldInput { node: CreatureCreateInput! } @@ -343,22 +415,6 @@ describe("Connection with interfaces", () => { create: [MovieDirectorCreateFieldInput!] } - input MovieDirectorNodeAggregationWhereInput { - AND: [MovieDirectorNodeAggregationWhereInput!] - NOT: MovieDirectorNodeAggregationWhereInput - OR: [MovieDirectorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - input MovieDirectorUpdateConnectionInput { node: CreatureUpdateInput } @@ -377,15 +433,6 @@ describe("Connection with interfaces", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -396,53 +443,55 @@ describe("Connection with interfaces", () => { input MovieUpdateInput { director: [MovieDirectorUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + director: CreatureRelationshipFilters directorAggregate: MovieDirectorAggregateInput + directorConnection: MovieDirectorConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_ALL: ProductionDirectorConnectionWhere + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_NONE: ProductionDirectorConnectionWhere + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SINGLE: ProductionDirectorConnectionWhere + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SOME: ProductionDirectorConnectionWhere + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Creatures match this filter\\"\\"\\" - director_ALL: CreatureWhere + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Creatures match this filter\\"\\"\\" - director_NONE: CreatureWhere + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Creatures match this filter\\"\\"\\" - director_SINGLE: CreatureWhere + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Creatures match this filter\\"\\"\\" - director_SOME: CreatureWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -479,14 +528,13 @@ describe("Connection with interfaces", () => { type Person implements Creature { id: ID - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): PersonProductionMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! + movies(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + moviesAggregate(where: ProductionWhere): PersonProductionMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! } type PersonAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PersonCreateInput { @@ -495,7 +543,7 @@ describe("Connection with interfaces", () => { } input PersonDeleteInput { - movies: PersonMoviesDeleteFieldInput + movies: [PersonMoviesDeleteFieldInput!] } type PersonEdge { @@ -507,13 +555,12 @@ describe("Connection with interfaces", () => { AND: [PersonMoviesAggregateInput!] NOT: PersonMoviesAggregateInput OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: PersonMoviesNodeAggregationWhereInput } input PersonMoviesConnectFieldInput { @@ -521,6 +568,25 @@ describe("Connection with interfaces", () => { where: ProductionConnectWhere } + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + all: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + none: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + single: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + some: CreatureMoviesConnectionWhere + } + input PersonMoviesCreateFieldInput { node: ProductionCreateInput! } @@ -536,24 +602,8 @@ describe("Connection with interfaces", () => { } input PersonMoviesFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - } - - input PersonMoviesNodeAggregationWhereInput { - AND: [PersonMoviesNodeAggregationWhereInput!] - NOT: PersonMoviesNodeAggregationWhereInput - OR: [PersonMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + connect: [PersonMoviesConnectFieldInput!] + create: [PersonMoviesCreateFieldInput!] } input PersonMoviesUpdateConnectionInput { @@ -561,30 +611,16 @@ describe("Connection with interfaces", () => { } input PersonMoviesUpdateFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - delete: PersonMoviesDeleteFieldInput - disconnect: PersonMoviesDisconnectFieldInput + connect: [PersonMoviesConnectFieldInput!] + create: [PersonMoviesCreateFieldInput!] + delete: [PersonMoviesDeleteFieldInput!] + disconnect: [PersonMoviesDisconnectFieldInput!] update: PersonMoviesUpdateConnectionInput where: CreatureMoviesConnectionWhere } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - type PersonProductionMoviesAggregationSelection { count: Int! - node: PersonProductionMoviesNodeAggregateSelection - } - - type PersonProductionMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } \\"\\"\\" @@ -595,35 +631,58 @@ describe("Connection with interfaces", () => { } input PersonUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - movies: PersonMoviesUpdateFieldInput + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [PersonMoviesUpdateFieldInput!] } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - movies: ProductionWhere + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: ProductionRelationshipFilters moviesAggregate: PersonMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere + moviesConnection: PersonMoviesConnectionFilters + \\"\\"\\" + Return People where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return People where all of the related Productions match this filter\\"\\"\\" + movies_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return People where none of the related Productions match this filter\\"\\"\\" + movies_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return People where one of the related Productions match this filter\\"\\"\\" + movies_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return People where some of the related Productions match this filter\\"\\"\\" + movies_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } interface Production { - director(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CreatureSort!], where: CreatureWhere): [Creature!]! + director(limit: Int, offset: Int, sort: [CreatureSort!], where: CreatureWhere): [Creature!]! directorConnection(after: String, first: Int, sort: [ProductionDirectorConnectionSort!], where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! id: ID } type ProductionAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ProductionConnectInput { @@ -647,13 +706,12 @@ describe("Connection with interfaces", () => { AND: [ProductionDirectorAggregateInput!] NOT: ProductionDirectorAggregateInput OR: [ProductionDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: ProductionDirectorNodeAggregationWhereInput } input ProductionDirectorConnectFieldInput { @@ -667,6 +725,25 @@ describe("Connection with interfaces", () => { totalCount: Int! } + input ProductionDirectorConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input ProductionDirectorConnectionSort { node: CreatureSort } @@ -692,22 +769,6 @@ describe("Connection with interfaces", () => { where: ProductionDirectorConnectionWhere } - input ProductionDirectorNodeAggregationWhereInput { - AND: [ProductionDirectorNodeAggregationWhereInput!] - NOT: ProductionDirectorNodeAggregationWhereInput - OR: [ProductionDirectorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type ProductionDirectorRelationship { cursor: String! node: Creature! @@ -740,13 +801,15 @@ describe("Connection with interfaces", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -758,55 +821,56 @@ describe("Connection with interfaces", () => { input ProductionUpdateInput { director: [ProductionDirectorUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + director: CreatureRelationshipFilters directorAggregate: ProductionDirectorAggregateInput + directorConnection: ProductionDirectorConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_ALL: ProductionDirectorConnectionWhere + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_NONE: ProductionDirectorConnectionWhere + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SINGLE: ProductionDirectorConnectionWhere + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SOME: ProductionDirectorConnectionWhere + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where all of the related Creatures match this filter \\"\\"\\" - director_ALL: CreatureWhere + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") \\"\\"\\" Return Productions where none of the related Creatures match this filter \\"\\"\\" - director_NONE: CreatureWhere + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") \\"\\"\\" Return Productions where one of the related Creatures match this filter \\"\\"\\" - director_SINGLE: CreatureWhere + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") \\"\\"\\" Return Productions where some of the related Creatures match this filter \\"\\"\\" - director_SOME: CreatureWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -816,27 +880,27 @@ describe("Connection with interfaces", () => { } type Query { - creatures(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CreatureSort!], where: CreatureWhere): [Creature!]! + creatures(limit: Int, offset: Int, sort: [CreatureSort!], where: CreatureWhere): [Creature!]! creaturesAggregate(where: CreatureWhere): CreatureAggregateSelection! creaturesConnection(after: String, first: Int, sort: [CreatureSort!], where: CreatureWhere): CreaturesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CreatureSort!], where: CreatureWhere): [Creature!]! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): SeriesCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionDirectorConnectionSort!], where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! + director(limit: Int, offset: Int, sort: [CreatureSort!], where: CreatureWhere): [Creature!]! + directorAggregate(where: CreatureWhere): SeriesCreatureDirectorAggregationSelection + directorConnection(after: String, first: Int, sort: [ProductionDirectorConnectionSort!], where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! episode: Int! id: ID title: String! @@ -845,7 +909,6 @@ describe("Connection with interfaces", () => { type SeriesAggregateSelection { count: Int! episode: IntAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -864,11 +927,6 @@ describe("Connection with interfaces", () => { type SeriesCreatureDirectorAggregationSelection { count: Int! - node: SeriesCreatureDirectorNodeAggregateSelection - } - - type SeriesCreatureDirectorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input SeriesDeleteInput { @@ -879,13 +937,12 @@ describe("Connection with interfaces", () => { AND: [SeriesDirectorAggregateInput!] NOT: SeriesDirectorAggregateInput OR: [SeriesDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: SeriesDirectorNodeAggregationWhereInput } input SeriesDirectorConnectFieldInput { @@ -893,6 +950,25 @@ describe("Connection with interfaces", () => { where: CreatureConnectWhere } + input SeriesDirectorConnectionFilters { + \\"\\"\\" + Return Series where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input SeriesDirectorCreateFieldInput { node: CreatureCreateInput! } @@ -912,22 +988,6 @@ describe("Connection with interfaces", () => { create: [SeriesDirectorCreateFieldInput!] } - input SeriesDirectorNodeAggregationWhereInput { - AND: [SeriesDirectorNodeAggregationWhereInput!] - NOT: SeriesDirectorNodeAggregationWhereInput - OR: [SeriesDirectorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - input SeriesDirectorUpdateConnectionInput { node: CreatureUpdateInput } @@ -946,15 +1006,6 @@ describe("Connection with interfaces", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -966,64 +1017,66 @@ describe("Connection with interfaces", () => { input SeriesUpdateInput { director: [SeriesDirectorUpdateFieldInput!] - episode: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episode_DECREMENT: Int - episode_INCREMENT: Int - episode_SET: Int - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episode: IntScalarMutations + episode_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episode: { decrement: ... } }' instead.\\") + episode_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episode: { increment: ... } }' instead.\\") + episode_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episode: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + director: CreatureRelationshipFilters directorAggregate: SeriesDirectorAggregateInput + directorConnection: SeriesDirectorConnectionFilters \\"\\"\\" Return Series where all of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_ALL: ProductionDirectorConnectionWhere + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_NONE: ProductionDirectorConnectionWhere + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SINGLE: ProductionDirectorConnectionWhere + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionDirectorConnections match this filter \\"\\"\\" - directorConnection_SOME: ProductionDirectorConnectionWhere + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Creatures match this filter\\"\\"\\" - director_ALL: CreatureWhere + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Creatures match this filter\\"\\"\\" - director_NONE: CreatureWhere + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Creatures match this filter\\"\\"\\" - director_SINGLE: CreatureWhere + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Creatures match this filter\\"\\"\\" - director_SOME: CreatureWhere - episode: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episode_EQ: Int - episode_GT: Int - episode_GTE: Int - episode_IN: [Int!] - episode_LT: Int - episode_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + episode: IntScalarFilters + episode_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { eq: ... }\\") + episode_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gt: ... }\\") + episode_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gte: ... }\\") + episode_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episode: { in: ... }\\") + episode_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lt: ... }\\") + episode_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -1039,6 +1092,20 @@ describe("Connection with interfaces", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/connections/sort.test.ts b/packages/graphql/tests/schema/connections/sort.test.ts index 52a681ac97..764fd9a2db 100644 --- a/packages/graphql/tests/schema/connections/sort.test.ts +++ b/packages/graphql/tests/schema/connections/sort.test.ts @@ -69,6 +69,26 @@ describe("Sort", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createNode1s(input: [Node1CreateInput!]!): CreateNode1sMutationResponse! createNode2s(input: [Node2CreateInput!]!): CreateNode2sMutationResponse! @@ -80,9 +100,9 @@ describe("Sort", () => { type Node1 { property: String! - relatedTo(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Node2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: Node2Where): [Node2!]! - relatedToAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Node2Where): Node1Node2RelatedToAggregationSelection - relatedToConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: Node1RelatedToConnectionWhere): Node1RelatedToConnection! + relatedTo(limit: Int, offset: Int, where: Node2Where): [Node2!]! + relatedToAggregate(where: Node2Where): Node1Node2RelatedToAggregationSelection + relatedToConnection(after: String, first: Int, where: Node1RelatedToConnectionWhere): Node1RelatedToConnection! } type Node1AggregateSelection { @@ -120,20 +140,11 @@ describe("Sort", () => { count: Int! } - input Node1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Node1Sort objects to sort Node1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Node1Sort!] - } - input Node1RelatedToAggregateInput { AND: [Node1RelatedToAggregateInput!] NOT: Node1RelatedToAggregateInput OR: [Node1RelatedToAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -143,10 +154,6 @@ describe("Sort", () => { input Node1RelatedToConnectFieldInput { connect: [Node2ConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: Node2ConnectWhere } @@ -156,6 +163,25 @@ describe("Sort", () => { totalCount: Int! } + input Node1RelatedToConnectionFilters { + \\"\\"\\" + Return Node1s where all of the related Node1RelatedToConnections match this filter + \\"\\"\\" + all: Node1RelatedToConnectionWhere + \\"\\"\\" + Return Node1s where none of the related Node1RelatedToConnections match this filter + \\"\\"\\" + none: Node1RelatedToConnectionWhere + \\"\\"\\" + Return Node1s where one of the related Node1RelatedToConnections match this filter + \\"\\"\\" + single: Node1RelatedToConnectionWhere + \\"\\"\\" + Return Node1s where some of the related Node1RelatedToConnections match this filter + \\"\\"\\" + some: Node1RelatedToConnectionWhere + } + input Node1RelatedToConnectionWhere { AND: [Node1RelatedToConnectionWhere!] NOT: Node1RelatedToConnectionWhere @@ -200,6 +226,17 @@ describe("Sort", () => { where: Node1RelatedToConnectionWhere } + input Node1RelationshipFilters { + \\"\\"\\"Filter type where all of the related Node1s match this filter\\"\\"\\" + all: Node1Where + \\"\\"\\"Filter type where none of the related Node1s match this filter\\"\\"\\" + none: Node1Where + \\"\\"\\"Filter type where one of the related Node1s match this filter\\"\\"\\" + single: Node1Where + \\"\\"\\"Filter type where some of the related Node1s match this filter\\"\\"\\" + some: Node1Where + } + \\"\\"\\" Fields to sort Node1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Node1Sort object. \\"\\"\\" @@ -208,8 +245,8 @@ describe("Sort", () => { } input Node1UpdateInput { - property: String @deprecated(reason: \\"Please use the explicit _SET field\\") - property_SET: String + property: StringScalarMutations + property_SET: String @deprecated(reason: \\"Please use the generic mutation 'property: { set: ... } }' instead.\\") relatedTo: [Node1RelatedToUpdateFieldInput!] } @@ -217,37 +254,39 @@ describe("Sort", () => { AND: [Node1Where!] NOT: Node1Where OR: [Node1Where!] - property: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - property_CONTAINS: String - property_ENDS_WITH: String - property_EQ: String - property_IN: [String!] - property_STARTS_WITH: String + property: StringScalarFilters + property_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter property: { contains: ... }\\") + property_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter property: { endsWith: ... }\\") + property_EQ: String @deprecated(reason: \\"Please use the relevant generic filter property: { eq: ... }\\") + property_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter property: { in: ... }\\") + property_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter property: { startsWith: ... }\\") + relatedTo: Node2RelationshipFilters relatedToAggregate: Node1RelatedToAggregateInput + relatedToConnection: Node1RelatedToConnectionFilters \\"\\"\\" Return Node1s where all of the related Node1RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_ALL: Node1RelatedToConnectionWhere + relatedToConnection_ALL: Node1RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Node1s where none of the related Node1RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_NONE: Node1RelatedToConnectionWhere + relatedToConnection_NONE: Node1RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Node1s where one of the related Node1RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_SINGLE: Node1RelatedToConnectionWhere + relatedToConnection_SINGLE: Node1RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Node1s where some of the related Node1RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_SOME: Node1RelatedToConnectionWhere + relatedToConnection_SOME: Node1RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Node1s where all of the related Node2s match this filter\\"\\"\\" - relatedTo_ALL: Node2Where + relatedTo_ALL: Node2Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { all: ... }' instead.\\") \\"\\"\\"Return Node1s where none of the related Node2s match this filter\\"\\"\\" - relatedTo_NONE: Node2Where + relatedTo_NONE: Node2Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { none: ... }' instead.\\") \\"\\"\\"Return Node1s where one of the related Node2s match this filter\\"\\"\\" - relatedTo_SINGLE: Node2Where + relatedTo_SINGLE: Node2Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { single: ... }' instead.\\") \\"\\"\\"Return Node1s where some of the related Node2s match this filter\\"\\"\\" - relatedTo_SOME: Node2Where + relatedTo_SOME: Node2Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { some: ... }' instead.\\") } type Node1sConnection { @@ -257,9 +296,9 @@ describe("Sort", () => { } type Node2 { - relatedTo(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Node1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Node1Sort!], where: Node1Where): [Node1!]! - relatedToAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Node1Where): Node2Node1RelatedToAggregationSelection - relatedToConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Node2RelatedToConnectionSort!], where: Node2RelatedToConnectionWhere): Node2RelatedToConnection! + relatedTo(limit: Int, offset: Int, sort: [Node1Sort!], where: Node1Where): [Node1!]! + relatedToAggregate(where: Node1Where): Node2Node1RelatedToAggregationSelection + relatedToConnection(after: String, first: Int, sort: [Node2RelatedToConnectionSort!], where: Node2RelatedToConnectionWhere): Node2RelatedToConnection! } type Node2AggregateSelection { @@ -300,16 +339,11 @@ describe("Sort", () => { property: StringAggregateSelection! } - input Node2Options { - limit: Int - offset: Int - } - input Node2RelatedToAggregateInput { AND: [Node2RelatedToAggregateInput!] NOT: Node2RelatedToAggregateInput OR: [Node2RelatedToAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -320,10 +354,6 @@ describe("Sort", () => { input Node2RelatedToConnectFieldInput { connect: [Node1ConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: Node1ConnectWhere } @@ -333,6 +363,25 @@ describe("Sort", () => { totalCount: Int! } + input Node2RelatedToConnectionFilters { + \\"\\"\\" + Return Node2s where all of the related Node2RelatedToConnections match this filter + \\"\\"\\" + all: Node2RelatedToConnectionWhere + \\"\\"\\" + Return Node2s where none of the related Node2RelatedToConnections match this filter + \\"\\"\\" + none: Node2RelatedToConnectionWhere + \\"\\"\\" + Return Node2s where one of the related Node2RelatedToConnections match this filter + \\"\\"\\" + single: Node2RelatedToConnectionWhere + \\"\\"\\" + Return Node2s where some of the related Node2RelatedToConnections match this filter + \\"\\"\\" + some: Node2RelatedToConnectionWhere + } + input Node2RelatedToConnectionSort { node: Node1Sort } @@ -367,21 +416,22 @@ describe("Sort", () => { AND: [Node2RelatedToNodeAggregationWhereInput!] NOT: Node2RelatedToNodeAggregationWhereInput OR: [Node2RelatedToNodeAggregationWhereInput!] - property_AVERAGE_LENGTH_EQUAL: Float - property_AVERAGE_LENGTH_GT: Float - property_AVERAGE_LENGTH_GTE: Float - property_AVERAGE_LENGTH_LT: Float - property_AVERAGE_LENGTH_LTE: Float - property_LONGEST_LENGTH_EQUAL: Int - property_LONGEST_LENGTH_GT: Int - property_LONGEST_LENGTH_GTE: Int - property_LONGEST_LENGTH_LT: Int - property_LONGEST_LENGTH_LTE: Int - property_SHORTEST_LENGTH_EQUAL: Int - property_SHORTEST_LENGTH_GT: Int - property_SHORTEST_LENGTH_GTE: Int - property_SHORTEST_LENGTH_LT: Int - property_SHORTEST_LENGTH_LTE: Int + property: StringScalarAggregationFilters + property_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'property: { averageLength: { eq: ... } } }' instead.\\") + property_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'property: { averageLength: { gt: ... } } }' instead.\\") + property_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'property: { averageLength: { gte: ... } } }' instead.\\") + property_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'property: { averageLength: { lt: ... } } }' instead.\\") + property_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'property: { averageLength: { lte: ... } } }' instead.\\") + property_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { longestLength: { eq: ... } } }' instead.\\") + property_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { longestLength: { gt: ... } } }' instead.\\") + property_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { longestLength: { gte: ... } } }' instead.\\") + property_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { longestLength: { lt: ... } } }' instead.\\") + property_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { longestLength: { lte: ... } } }' instead.\\") + property_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { shortestLength: { eq: ... } } }' instead.\\") + property_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { shortestLength: { gt: ... } } }' instead.\\") + property_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { shortestLength: { gte: ... } } }' instead.\\") + property_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { shortestLength: { lt: ... } } }' instead.\\") + property_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'property: { shortestLength: { lte: ... } } }' instead.\\") } type Node2RelatedToRelationship { @@ -402,6 +452,17 @@ describe("Sort", () => { where: Node2RelatedToConnectionWhere } + input Node2RelationshipFilters { + \\"\\"\\"Filter type where all of the related Node2s match this filter\\"\\"\\" + all: Node2Where + \\"\\"\\"Filter type where none of the related Node2s match this filter\\"\\"\\" + none: Node2Where + \\"\\"\\"Filter type where one of the related Node2s match this filter\\"\\"\\" + single: Node2Where + \\"\\"\\"Filter type where some of the related Node2s match this filter\\"\\"\\" + some: Node2Where + } + input Node2UpdateInput { relatedTo: [Node2RelatedToUpdateFieldInput!] } @@ -410,31 +471,33 @@ describe("Sort", () => { AND: [Node2Where!] NOT: Node2Where OR: [Node2Where!] + relatedTo: Node1RelationshipFilters relatedToAggregate: Node2RelatedToAggregateInput + relatedToConnection: Node2RelatedToConnectionFilters \\"\\"\\" Return Node2s where all of the related Node2RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_ALL: Node2RelatedToConnectionWhere + relatedToConnection_ALL: Node2RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Node2s where none of the related Node2RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_NONE: Node2RelatedToConnectionWhere + relatedToConnection_NONE: Node2RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Node2s where one of the related Node2RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_SINGLE: Node2RelatedToConnectionWhere + relatedToConnection_SINGLE: Node2RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Node2s where some of the related Node2RelatedToConnections match this filter \\"\\"\\" - relatedToConnection_SOME: Node2RelatedToConnectionWhere + relatedToConnection_SOME: Node2RelatedToConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relatedToConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Node2s where all of the related Node1s match this filter\\"\\"\\" - relatedTo_ALL: Node1Where + relatedTo_ALL: Node1Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { all: ... }' instead.\\") \\"\\"\\"Return Node2s where none of the related Node1s match this filter\\"\\"\\" - relatedTo_NONE: Node1Where + relatedTo_NONE: Node1Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { none: ... }' instead.\\") \\"\\"\\"Return Node2s where one of the related Node1s match this filter\\"\\"\\" - relatedTo_SINGLE: Node1Where + relatedTo_SINGLE: Node1Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { single: ... }' instead.\\") \\"\\"\\"Return Node2s where some of the related Node1s match this filter\\"\\"\\" - relatedTo_SOME: Node1Where + relatedTo_SOME: Node1Where @deprecated(reason: \\"Please use the relevant generic filter 'relatedTo: { some: ... }' instead.\\") } type Node2sConnection { @@ -452,10 +515,10 @@ describe("Sort", () => { } type Query { - node1s(limit: Int, offset: Int, options: Node1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Node1Sort!], where: Node1Where): [Node1!]! + node1s(limit: Int, offset: Int, sort: [Node1Sort!], where: Node1Where): [Node1!]! node1sAggregate(where: Node1Where): Node1AggregateSelection! node1sConnection(after: String, first: Int, sort: [Node1Sort!], where: Node1Where): Node1sConnection! - node2s(limit: Int, offset: Int, options: Node2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: Node2Where): [Node2!]! + node2s(limit: Int, offset: Int, where: Node2Where): [Node2!]! node2sAggregate(where: Node2Where): Node2AggregateSelection! node2sConnection(after: String, first: Int, where: Node2Where): Node2sConnection! } @@ -473,6 +536,27 @@ describe("Sort", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/connections/unions.test.ts b/packages/graphql/tests/schema/connections/unions.test.ts index f77ff546d9..8e3764ae32 100644 --- a/packages/graphql/tests/schema/connections/unions.test.ts +++ b/packages/graphql/tests/schema/connections/unions.test.ts @@ -57,8 +57,8 @@ describe("Unions", () => { type Author { name: String! - publications(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PublicationWhere): [Publication!]! - publicationsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [AuthorPublicationsConnectionSort!], where: AuthorPublicationsConnectionWhere): AuthorPublicationsConnection! + publications(limit: Int, offset: Int, where: PublicationWhere): [Publication!]! + publicationsConnection(after: String, first: Int, sort: [AuthorPublicationsConnectionSort!], where: AuthorPublicationsConnectionWhere): AuthorPublicationsConnection! } type AuthorAggregateSelection { @@ -92,15 +92,6 @@ describe("Unions", () => { node: Author! } - input AuthorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AuthorSort objects to sort Authors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AuthorSort!] - } - input AuthorPublicationsBookConnectFieldInput { connect: [BookConnectInput!] edge: WroteCreateInput! @@ -160,6 +151,25 @@ describe("Unions", () => { totalCount: Int! } + input AuthorPublicationsConnectionFilters { + \\"\\"\\" + Return Authors where all of the related AuthorPublicationsConnections match this filter + \\"\\"\\" + all: AuthorPublicationsConnectionWhere + \\"\\"\\" + Return Authors where none of the related AuthorPublicationsConnections match this filter + \\"\\"\\" + none: AuthorPublicationsConnectionWhere + \\"\\"\\" + Return Authors where one of the related AuthorPublicationsConnections match this filter + \\"\\"\\" + single: AuthorPublicationsConnectionWhere + \\"\\"\\" + Return Authors where some of the related AuthorPublicationsConnections match this filter + \\"\\"\\" + some: AuthorPublicationsConnectionWhere + } + input AuthorPublicationsConnectionSort { edge: WroteSort } @@ -243,6 +253,17 @@ describe("Unions", () => { Journal: [AuthorPublicationsJournalUpdateFieldInput!] } + input AuthorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Authors match this filter\\"\\"\\" + all: AuthorWhere + \\"\\"\\"Filter type where none of the related Authors match this filter\\"\\"\\" + none: AuthorWhere + \\"\\"\\"Filter type where one of the related Authors match this filter\\"\\"\\" + single: AuthorWhere + \\"\\"\\"Filter type where some of the related Authors match this filter\\"\\"\\" + some: AuthorWhere + } + \\"\\"\\" Fields to sort Authors by. The order in which sorts are applied is not guaranteed when specifying many fields in one AuthorSort object. \\"\\"\\" @@ -251,8 +272,8 @@ describe("Unions", () => { } input AuthorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") publications: AuthorPublicationsUpdateInput } @@ -260,40 +281,42 @@ describe("Unions", () => { AND: [AuthorWhere!] NOT: AuthorWhere OR: [AuthorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + publications: PublicationRelationshipFilters + publicationsConnection: AuthorPublicationsConnectionFilters \\"\\"\\" Return Authors where all of the related AuthorPublicationsConnections match this filter \\"\\"\\" - publicationsConnection_ALL: AuthorPublicationsConnectionWhere + publicationsConnection_ALL: AuthorPublicationsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'publicationsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Authors where none of the related AuthorPublicationsConnections match this filter \\"\\"\\" - publicationsConnection_NONE: AuthorPublicationsConnectionWhere + publicationsConnection_NONE: AuthorPublicationsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'publicationsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Authors where one of the related AuthorPublicationsConnections match this filter \\"\\"\\" - publicationsConnection_SINGLE: AuthorPublicationsConnectionWhere + publicationsConnection_SINGLE: AuthorPublicationsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'publicationsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Authors where some of the related AuthorPublicationsConnections match this filter \\"\\"\\" - publicationsConnection_SOME: AuthorPublicationsConnectionWhere + publicationsConnection_SOME: AuthorPublicationsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'publicationsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Authors where all of the related Publications match this filter\\"\\"\\" - publications_ALL: PublicationWhere + publications_ALL: PublicationWhere @deprecated(reason: \\"Please use the relevant generic filter 'publications: { all: ... }' instead.\\") \\"\\"\\" Return Authors where none of the related Publications match this filter \\"\\"\\" - publications_NONE: PublicationWhere + publications_NONE: PublicationWhere @deprecated(reason: \\"Please use the relevant generic filter 'publications: { none: ... }' instead.\\") \\"\\"\\"Return Authors where one of the related Publications match this filter\\"\\"\\" - publications_SINGLE: PublicationWhere + publications_SINGLE: PublicationWhere @deprecated(reason: \\"Please use the relevant generic filter 'publications: { single: ... }' instead.\\") \\"\\"\\" Return Authors where some of the related Publications match this filter \\"\\"\\" - publications_SOME: PublicationWhere + publications_SOME: PublicationWhere @deprecated(reason: \\"Please use the relevant generic filter 'publications: { some: ... }' instead.\\") } type AuthorsConnection { @@ -303,9 +326,9 @@ describe("Unions", () => { } type Book { - author(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: AuthorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AuthorSort!], where: AuthorWhere): [Author!]! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: AuthorWhere): BookAuthorAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [BookAuthorConnectionSort!], where: BookAuthorConnectionWhere): BookAuthorConnection! + author(limit: Int, offset: Int, sort: [AuthorSort!], where: AuthorWhere): [Author!]! + authorAggregate(where: AuthorWhere): BookAuthorAuthorAggregationSelection + authorConnection(after: String, first: Int, sort: [BookAuthorConnectionSort!], where: BookAuthorConnectionWhere): BookAuthorConnection! title: String! } @@ -318,7 +341,7 @@ describe("Unions", () => { AND: [BookAuthorAggregateInput!] NOT: BookAuthorAggregateInput OR: [BookAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -345,10 +368,6 @@ describe("Unions", () => { input BookAuthorConnectFieldInput { connect: [AuthorConnectInput!] edge: WroteCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: AuthorConnectWhere } @@ -358,6 +377,25 @@ describe("Unions", () => { totalCount: Int! } + input BookAuthorConnectionFilters { + \\"\\"\\" + Return Books where all of the related BookAuthorConnections match this filter + \\"\\"\\" + all: BookAuthorConnectionWhere + \\"\\"\\" + Return Books where none of the related BookAuthorConnections match this filter + \\"\\"\\" + none: BookAuthorConnectionWhere + \\"\\"\\" + Return Books where one of the related BookAuthorConnections match this filter + \\"\\"\\" + single: BookAuthorConnectionWhere + \\"\\"\\" + Return Books where some of the related BookAuthorConnections match this filter + \\"\\"\\" + some: BookAuthorConnectionWhere + } + input BookAuthorConnectionSort { edge: WroteSort node: AuthorSort @@ -395,21 +433,22 @@ describe("Unions", () => { AND: [BookAuthorNodeAggregationWhereInput!] NOT: BookAuthorNodeAggregationWhereInput OR: [BookAuthorNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type BookAuthorRelationship { @@ -458,15 +497,6 @@ describe("Unions", () => { node: Book! } - input BookOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BookSort objects to sort Books by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BookSort!] - } - \\"\\"\\" Fields to sort Books by. The order in which sorts are applied is not guaranteed when specifying many fields in one BookSort object. \\"\\"\\" @@ -476,45 +506,47 @@ describe("Unions", () => { input BookUpdateInput { author: [BookAuthorUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input BookWhere { AND: [BookWhere!] NOT: BookWhere OR: [BookWhere!] + author: AuthorRelationshipFilters authorAggregate: BookAuthorAggregateInput + authorConnection: BookAuthorConnectionFilters \\"\\"\\" Return Books where all of the related BookAuthorConnections match this filter \\"\\"\\" - authorConnection_ALL: BookAuthorConnectionWhere + authorConnection_ALL: BookAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Books where none of the related BookAuthorConnections match this filter \\"\\"\\" - authorConnection_NONE: BookAuthorConnectionWhere + authorConnection_NONE: BookAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Books where one of the related BookAuthorConnections match this filter \\"\\"\\" - authorConnection_SINGLE: BookAuthorConnectionWhere + authorConnection_SINGLE: BookAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Books where some of the related BookAuthorConnections match this filter \\"\\"\\" - authorConnection_SOME: BookAuthorConnectionWhere + authorConnection_SOME: BookAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Books where all of the related Authors match this filter\\"\\"\\" - author_ALL: AuthorWhere + author_ALL: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { all: ... }' instead.\\") \\"\\"\\"Return Books where none of the related Authors match this filter\\"\\"\\" - author_NONE: AuthorWhere + author_NONE: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { none: ... }' instead.\\") \\"\\"\\"Return Books where one of the related Authors match this filter\\"\\"\\" - author_SINGLE: AuthorWhere + author_SINGLE: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { single: ... }' instead.\\") \\"\\"\\"Return Books where some of the related Authors match this filter\\"\\"\\" - author_SOME: AuthorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + author_SOME: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type BooksConnection { @@ -554,6 +586,16 @@ describe("Unions", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -561,10 +603,35 @@ describe("Unions", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Journal { - author(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: AuthorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AuthorSort!], where: AuthorWhere): [Author!]! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: AuthorWhere): JournalAuthorAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [JournalAuthorConnectionSort!], where: JournalAuthorConnectionWhere): JournalAuthorConnection! + author(limit: Int, offset: Int, sort: [AuthorSort!], where: AuthorWhere): [Author!]! + authorAggregate(where: AuthorWhere): JournalAuthorAuthorAggregationSelection + authorConnection(after: String, first: Int, sort: [JournalAuthorConnectionSort!], where: JournalAuthorConnectionWhere): JournalAuthorConnection! subject: String! } @@ -577,7 +644,7 @@ describe("Unions", () => { AND: [JournalAuthorAggregateInput!] NOT: JournalAuthorAggregateInput OR: [JournalAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -604,10 +671,6 @@ describe("Unions", () => { input JournalAuthorConnectFieldInput { connect: [AuthorConnectInput!] edge: WroteCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: AuthorConnectWhere } @@ -617,6 +680,25 @@ describe("Unions", () => { totalCount: Int! } + input JournalAuthorConnectionFilters { + \\"\\"\\" + Return Journals where all of the related JournalAuthorConnections match this filter + \\"\\"\\" + all: JournalAuthorConnectionWhere + \\"\\"\\" + Return Journals where none of the related JournalAuthorConnections match this filter + \\"\\"\\" + none: JournalAuthorConnectionWhere + \\"\\"\\" + Return Journals where one of the related JournalAuthorConnections match this filter + \\"\\"\\" + single: JournalAuthorConnectionWhere + \\"\\"\\" + Return Journals where some of the related JournalAuthorConnections match this filter + \\"\\"\\" + some: JournalAuthorConnectionWhere + } + input JournalAuthorConnectionSort { edge: WroteSort node: AuthorSort @@ -654,21 +736,22 @@ describe("Unions", () => { AND: [JournalAuthorNodeAggregationWhereInput!] NOT: JournalAuthorNodeAggregationWhereInput OR: [JournalAuthorNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type JournalAuthorRelationship { @@ -717,15 +800,6 @@ describe("Unions", () => { node: Journal! } - input JournalOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more JournalSort objects to sort Journals by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [JournalSort!] - } - \\"\\"\\" Fields to sort Journals by. The order in which sorts are applied is not guaranteed when specifying many fields in one JournalSort object. \\"\\"\\" @@ -735,45 +809,47 @@ describe("Unions", () => { input JournalUpdateInput { author: [JournalAuthorUpdateFieldInput!] - subject: String @deprecated(reason: \\"Please use the explicit _SET field\\") - subject_SET: String + subject: StringScalarMutations + subject_SET: String @deprecated(reason: \\"Please use the generic mutation 'subject: { set: ... } }' instead.\\") } input JournalWhere { AND: [JournalWhere!] NOT: JournalWhere OR: [JournalWhere!] + author: AuthorRelationshipFilters authorAggregate: JournalAuthorAggregateInput + authorConnection: JournalAuthorConnectionFilters \\"\\"\\" Return Journals where all of the related JournalAuthorConnections match this filter \\"\\"\\" - authorConnection_ALL: JournalAuthorConnectionWhere + authorConnection_ALL: JournalAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Journals where none of the related JournalAuthorConnections match this filter \\"\\"\\" - authorConnection_NONE: JournalAuthorConnectionWhere + authorConnection_NONE: JournalAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Journals where one of the related JournalAuthorConnections match this filter \\"\\"\\" - authorConnection_SINGLE: JournalAuthorConnectionWhere + authorConnection_SINGLE: JournalAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Journals where some of the related JournalAuthorConnections match this filter \\"\\"\\" - authorConnection_SOME: JournalAuthorConnectionWhere + authorConnection_SOME: JournalAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Journals where all of the related Authors match this filter\\"\\"\\" - author_ALL: AuthorWhere + author_ALL: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { all: ... }' instead.\\") \\"\\"\\"Return Journals where none of the related Authors match this filter\\"\\"\\" - author_NONE: AuthorWhere + author_NONE: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { none: ... }' instead.\\") \\"\\"\\"Return Journals where one of the related Authors match this filter\\"\\"\\" - author_SINGLE: AuthorWhere + author_SINGLE: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { single: ... }' instead.\\") \\"\\"\\"Return Journals where some of the related Authors match this filter\\"\\"\\" - author_SOME: AuthorWhere - subject: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - subject_CONTAINS: String - subject_ENDS_WITH: String - subject_EQ: String - subject_IN: [String!] - subject_STARTS_WITH: String + author_SOME: AuthorWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { some: ... }' instead.\\") + subject: StringScalarFilters + subject_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter subject: { contains: ... }\\") + subject_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter subject: { endsWith: ... }\\") + subject_EQ: String @deprecated(reason: \\"Please use the relevant generic filter subject: { eq: ... }\\") + subject_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter subject: { in: ... }\\") + subject_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter subject: { startsWith: ... }\\") } type JournalsConnection { @@ -804,28 +880,33 @@ describe("Unions", () => { union Publication = Book | Journal + input PublicationRelationshipFilters { + \\"\\"\\"Filter type where all of the related Publications match this filter\\"\\"\\" + all: PublicationWhere + \\"\\"\\"Filter type where none of the related Publications match this filter\\"\\"\\" + none: PublicationWhere + \\"\\"\\"Filter type where one of the related Publications match this filter\\"\\"\\" + single: PublicationWhere + \\"\\"\\"Filter type where some of the related Publications match this filter\\"\\"\\" + some: PublicationWhere + } + input PublicationWhere { Book: BookWhere Journal: JournalWhere } type Query { - authors(limit: Int, offset: Int, options: AuthorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AuthorSort!], where: AuthorWhere): [Author!]! + authors(limit: Int, offset: Int, sort: [AuthorSort!], where: AuthorWhere): [Author!]! authorsAggregate(where: AuthorWhere): AuthorAggregateSelection! authorsConnection(after: String, first: Int, sort: [AuthorSort!], where: AuthorWhere): AuthorsConnection! - books(limit: Int, offset: Int, options: BookOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookSort!], where: BookWhere): [Book!]! + books(limit: Int, offset: Int, sort: [BookSort!], where: BookWhere): [Book!]! booksAggregate(where: BookWhere): BookAggregateSelection! booksConnection(after: String, first: Int, sort: [BookSort!], where: BookWhere): BooksConnection! - journals(limit: Int, offset: Int, options: JournalOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [JournalSort!], where: JournalWhere): [Journal!]! + journals(limit: Int, offset: Int, sort: [JournalSort!], where: JournalWhere): [Journal!]! journalsAggregate(where: JournalWhere): JournalAggregateSelection! journalsConnection(after: String, first: Int, sort: [JournalSort!], where: JournalWhere): JournalsConnection! - publications(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PublicationWhere): [Publication!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + publications(limit: Int, offset: Int, where: PublicationWhere): [Publication!]! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -841,6 +922,27 @@ describe("Unions", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateAuthorsMutationResponse { authors: [Author!]! info: UpdateInfo! @@ -880,26 +982,27 @@ describe("Unions", () => { AND: [WroteAggregationWhereInput!] NOT: WroteAggregationWhereInput OR: [WroteAggregationWhereInput!] - words_AVERAGE_EQUAL: Float - words_AVERAGE_GT: Float - words_AVERAGE_GTE: Float - words_AVERAGE_LT: Float - words_AVERAGE_LTE: Float - words_MAX_EQUAL: Int - words_MAX_GT: Int - words_MAX_GTE: Int - words_MAX_LT: Int - words_MAX_LTE: Int - words_MIN_EQUAL: Int - words_MIN_GT: Int - words_MIN_GTE: Int - words_MIN_LT: Int - words_MIN_LTE: Int - words_SUM_EQUAL: Int - words_SUM_GT: Int - words_SUM_GTE: Int - words_SUM_LT: Int - words_SUM_LTE: Int + words: IntScalarAggregationFilters + words_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'words: { average: { eq: ... } } }' instead.\\") + words_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'words: { average: { gt: ... } } }' instead.\\") + words_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'words: { average: { gte: ... } } }' instead.\\") + words_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'words: { average: { lt: ... } } }' instead.\\") + words_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'words: { average: { lte: ... } } }' instead.\\") + words_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { max: { eq: ... } } }' instead.\\") + words_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { max: { gt: ... } } }' instead.\\") + words_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { max: { gte: ... } } }' instead.\\") + words_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { max: { lt: ... } } }' instead.\\") + words_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { max: { lte: ... } } }' instead.\\") + words_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { min: { eq: ... } } }' instead.\\") + words_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { min: { gt: ... } } }' instead.\\") + words_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { min: { gte: ... } } }' instead.\\") + words_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { min: { lt: ... } } }' instead.\\") + words_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { min: { lte: ... } } }' instead.\\") + words_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { sum: { eq: ... } } }' instead.\\") + words_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { sum: { gt: ... } } }' instead.\\") + words_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { sum: { gte: ... } } }' instead.\\") + words_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { sum: { lt: ... } } }' instead.\\") + words_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'words: { sum: { lte: ... } } }' instead.\\") } input WroteCreateInput { @@ -911,23 +1014,23 @@ describe("Unions", () => { } input WroteUpdateInput { - words: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - words_DECREMENT: Int - words_INCREMENT: Int - words_SET: Int + words: IntScalarMutations + words_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'words: { decrement: ... } }' instead.\\") + words_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'words: { increment: ... } }' instead.\\") + words_SET: Int @deprecated(reason: \\"Please use the generic mutation 'words: { set: ... } }' instead.\\") } input WroteWhere { AND: [WroteWhere!] NOT: WroteWhere OR: [WroteWhere!] - words: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - words_EQ: Int - words_GT: Int - words_GTE: Int - words_IN: [Int!] - words_LT: Int - words_LTE: Int + words: IntScalarFilters + words_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter words: { eq: ... }\\") + words_GT: Int @deprecated(reason: \\"Please use the relevant generic filter words: { gt: ... }\\") + words_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter words: { gte: ... }\\") + words_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter words: { in: ... }\\") + words_LT: Int @deprecated(reason: \\"Please use the relevant generic filter words: { lt: ... }\\") + words_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter words: { lte: ... }\\") }" `); }); diff --git a/packages/graphql/tests/schema/custom-mutations.test.ts b/packages/graphql/tests/schema/custom-mutations.test.ts index 3680a0dcae..ca4df6f23f 100644 --- a/packages/graphql/tests/schema/custom-mutations.test.ts +++ b/packages/graphql/tests/schema/custom-mutations.test.ts @@ -83,9 +83,18 @@ describe("Custom-mutations", () => { id: ID } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -94,7 +103,6 @@ describe("Custom-mutations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -106,15 +114,6 @@ describe("Custom-mutations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -123,20 +122,20 @@ describe("Custom-mutations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -162,7 +161,7 @@ describe("Custom-mutations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! testCypherQuery(input: ExampleInput): String diff --git a/packages/graphql/tests/schema/directive-preserve.test.ts b/packages/graphql/tests/schema/directive-preserve.test.ts index cf24230abf..360f8db2a2 100644 --- a/packages/graphql/tests/schema/directive-preserve.test.ts +++ b/packages/graphql/tests/schema/directive-preserve.test.ts @@ -67,9 +67,18 @@ describe("Directive-preserve", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie @preservedTopLevel { @@ -78,7 +87,6 @@ describe("Directive-preserve", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -90,15 +98,6 @@ describe("Directive-preserve", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -107,7 +106,7 @@ describe("Directive-preserve", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") + id: IDScalarMutations id_SET: ID } @@ -115,12 +114,12 @@ describe("Directive-preserve", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -144,7 +143,7 @@ describe("Directive-preserve", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -230,10 +229,37 @@ describe("Directive-preserve", () => { sum: Float } + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + type Genre { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): GenreMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): GenreMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! name: String } @@ -283,7 +309,7 @@ describe("Directive-preserve", () => { AND: [GenreMoviesAggregateInput!] NOT: GenreMoviesAggregateInput OR: [GenreMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -294,10 +320,6 @@ describe("Directive-preserve", () => { input GenreMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -307,6 +329,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input GenreMoviesConnectionFilters { + \\"\\"\\" + Return Genres where all of the related GenreMoviesConnections match this filter + \\"\\"\\" + all: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where none of the related GenreMoviesConnections match this filter + \\"\\"\\" + none: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where one of the related GenreMoviesConnections match this filter + \\"\\"\\" + single: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where some of the related GenreMoviesConnections match this filter + \\"\\"\\" + some: GenreMoviesConnectionWhere + } + input GenreMoviesConnectionSort { node: MovieSort } @@ -341,61 +382,64 @@ describe("Directive-preserve", () => { AND: [GenreMoviesNodeAggregationWhereInput!] NOT: GenreMoviesNodeAggregationWhereInput OR: [GenreMoviesNodeAggregationWhereInput!] - imdbRating_AVERAGE_EQUAL: Float - imdbRating_AVERAGE_GT: Float - imdbRating_AVERAGE_GTE: Float - imdbRating_AVERAGE_LT: Float - imdbRating_AVERAGE_LTE: Float - imdbRating_MAX_EQUAL: Float - imdbRating_MAX_GT: Float - imdbRating_MAX_GTE: Float - imdbRating_MAX_LT: Float - imdbRating_MAX_LTE: Float - imdbRating_MIN_EQUAL: Float - imdbRating_MIN_GT: Float - imdbRating_MIN_GTE: Float - imdbRating_MIN_LT: Float - imdbRating_MIN_LTE: Float - imdbRating_SUM_EQUAL: Float - imdbRating_SUM_GT: Float - imdbRating_SUM_GTE: Float - imdbRating_SUM_LT: Float - imdbRating_SUM_LTE: Float - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - year_AVERAGE_EQUAL: Float - year_AVERAGE_GT: Float - year_AVERAGE_GTE: Float - year_AVERAGE_LT: Float - year_AVERAGE_LTE: Float - year_MAX_EQUAL: Int - year_MAX_GT: Int - year_MAX_GTE: Int - year_MAX_LT: Int - year_MAX_LTE: Int - year_MIN_EQUAL: Int - year_MIN_GT: Int - year_MIN_GTE: Int - year_MIN_LT: Int - year_MIN_LTE: Int - year_SUM_EQUAL: Int - year_SUM_GT: Int - year_SUM_GTE: Int - year_SUM_LT: Int - year_SUM_LTE: Int + imdbRating: FloatScalarAggregationFilters + imdbRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { eq: ... } } }' instead.\\") + imdbRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { gt: ... } } }' instead.\\") + imdbRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { gte: ... } } }' instead.\\") + imdbRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { lt: ... } } }' instead.\\") + imdbRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { lte: ... } } }' instead.\\") + imdbRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { eq: ... } } }' instead.\\") + imdbRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { gt: ... } } }' instead.\\") + imdbRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { gte: ... } } }' instead.\\") + imdbRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { lt: ... } } }' instead.\\") + imdbRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { lte: ... } } }' instead.\\") + imdbRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { eq: ... } } }' instead.\\") + imdbRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { gt: ... } } }' instead.\\") + imdbRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { gte: ... } } }' instead.\\") + imdbRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { lt: ... } } }' instead.\\") + imdbRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { lte: ... } } }' instead.\\") + imdbRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { eq: ... } } }' instead.\\") + imdbRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { gt: ... } } }' instead.\\") + imdbRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { gte: ... } } }' instead.\\") + imdbRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { lt: ... } } }' instead.\\") + imdbRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + year: IntScalarAggregationFilters + year_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { eq: ... } } }' instead.\\") + year_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gt: ... } } }' instead.\\") + year_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gte: ... } } }' instead.\\") + year_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lt: ... } } }' instead.\\") + year_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lte: ... } } }' instead.\\") + year_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { eq: ... } } }' instead.\\") + year_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gt: ... } } }' instead.\\") + year_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gte: ... } } }' instead.\\") + year_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lt: ... } } }' instead.\\") + year_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lte: ... } } }' instead.\\") + year_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { eq: ... } } }' instead.\\") + year_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gt: ... } } }' instead.\\") + year_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gte: ... } } }' instead.\\") + year_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lt: ... } } }' instead.\\") + year_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lte: ... } } }' instead.\\") + year_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { eq: ... } } }' instead.\\") + year_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gt: ... } } }' instead.\\") + year_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gte: ... } } }' instead.\\") + year_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lt: ... } } }' instead.\\") + year_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lte: ... } } }' instead.\\") } type GenreMoviesRelationship { @@ -416,13 +460,15 @@ describe("Directive-preserve", () => { where: GenreMoviesConnectionWhere } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] + input GenreRelationshipFilters { + \\"\\"\\"Filter type where all of the related Genres match this filter\\"\\"\\" + all: GenreWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where none of the related Genres match this filter\\"\\"\\" + none: GenreWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where one of the related Genres match this filter\\"\\"\\" + single: GenreWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where some of the related Genres match this filter\\"\\"\\" + some: GenreWhere @deprecated(reason: \\"Do not use\\") } \\"\\"\\" @@ -434,45 +480,47 @@ describe("Directive-preserve", () => { input GenreUpdateInput { movies: [GenreMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] + movies: MovieRelationshipFilters moviesAggregate: GenreMoviesAggregateInput + moviesConnection: GenreMoviesConnectionFilters \\"\\"\\" Return Genres where all of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: GenreMoviesConnectionWhere + moviesConnection_ALL: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where none of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: GenreMoviesConnectionWhere + moviesConnection_NONE: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where one of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: GenreMoviesConnectionWhere + moviesConnection_SINGLE: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where some of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: GenreMoviesConnectionWhere + moviesConnection_SOME: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Genres where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Genres where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Genres where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Genres where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type GenresConnection { @@ -488,10 +536,35 @@ describe("Directive-preserve", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - genres(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! @deprecated(reason: \\"Do not use\\") - genresAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenresAggregationSelection @deprecated(reason: \\"Do not use\\") - genresConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! @deprecated(reason: \\"Do not use\\") + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! @deprecated(reason: \\"Do not use\\") + genresAggregate(where: GenreWhere): MovieGenreGenresAggregationSelection @deprecated(reason: \\"Do not use\\") + genresConnection(after: String, first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! @deprecated(reason: \\"Do not use\\") imdbRating: Float title: String year: Int @@ -545,7 +618,7 @@ describe("Directive-preserve", () => { AND: [MovieGenresAggregateInput!] NOT: MovieGenresAggregateInput OR: [MovieGenresAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -556,10 +629,6 @@ describe("Directive-preserve", () => { input MovieGenresConnectFieldInput { connect: [GenreConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: GenreConnectWhere } @@ -569,6 +638,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input MovieGenresConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieGenresConnections match this filter + \\"\\"\\" + all: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where none of the related MovieGenresConnections match this filter + \\"\\"\\" + none: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where one of the related MovieGenresConnections match this filter + \\"\\"\\" + single: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where some of the related MovieGenresConnections match this filter + \\"\\"\\" + some: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use\\") + } + input MovieGenresConnectionSort { node: GenreSort } @@ -603,21 +691,22 @@ describe("Directive-preserve", () => { AND: [MovieGenresNodeAggregationWhereInput!] NOT: MovieGenresNodeAggregationWhereInput OR: [MovieGenresNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieGenresRelationship { @@ -638,13 +727,15 @@ describe("Directive-preserve", () => { where: MovieGenresConnectionWhere } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -658,25 +749,27 @@ describe("Directive-preserve", () => { input MovieUpdateInput { genres: [MovieGenresUpdateFieldInput!] @deprecated(reason: \\"Do not use\\") - imdbRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - imdbRating_ADD: Float - imdbRating_DIVIDE: Float - imdbRating_MULTIPLY: Float - imdbRating_SET: Float - imdbRating_SUBTRACT: Float - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int + imdbRating: FloatScalarMutations + imdbRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { add: ... } }' instead.\\") + imdbRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { divide: ... } }' instead.\\") + imdbRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { multiply: ... } }' instead.\\") + imdbRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'imdbRating: { set: ... } }' instead.\\") + imdbRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { subtract: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + year: IntScalarMutations + year_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { decrement: ... } }' instead.\\") + year_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { increment: ... } }' instead.\\") + year_SET: Int @deprecated(reason: \\"Please use the generic mutation 'year: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + genres: GenreRelationshipFilters genresAggregate: MovieGenresAggregateInput @deprecated(reason: \\"Do not use\\") + genresConnection: MovieGenresConnectionFilters \\"\\"\\" Return Movies where all of the related MovieGenresConnections match this filter \\"\\"\\" @@ -701,26 +794,26 @@ describe("Directive-preserve", () => { genres_SINGLE: GenreWhere @deprecated(reason: \\"Do not use\\") \\"\\"\\"Return Movies where some of the related Genres match this filter\\"\\"\\" genres_SOME: GenreWhere @deprecated(reason: \\"Do not use\\") - imdbRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbRating_EQ: Float - imdbRating_GT: Float - imdbRating_GTE: Float - imdbRating_IN: [Float] - imdbRating_LT: Float - imdbRating_LTE: Float - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int] - year_LT: Int - year_LTE: Int + imdbRating: FloatScalarFilters + imdbRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { eq: ... }\\") + imdbRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gt: ... }\\") + imdbRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gte: ... }\\") + imdbRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { in: ... }\\") + imdbRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lt: ... }\\") + imdbRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + year: IntScalarFilters + year_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter year: { eq: ... }\\") + year_GT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gt: ... }\\") + year_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gte: ... }\\") + year_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter year: { in: ... }\\") + year_LT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lt: ... }\\") + year_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lte: ... }\\") } type MoviesConnection { @@ -747,10 +840,10 @@ describe("Directive-preserve", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -768,6 +861,27 @@ describe("Directive-preserve", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateGenresMutationResponse { genres: [Genre!]! info: UpdateInfo! @@ -852,21 +966,22 @@ describe("Directive-preserve", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - role_AVERAGE_LENGTH_EQUAL: Float - role_AVERAGE_LENGTH_GT: Float - role_AVERAGE_LENGTH_GTE: Float - role_AVERAGE_LENGTH_LT: Float - role_AVERAGE_LENGTH_LTE: Float - role_LONGEST_LENGTH_EQUAL: Int - role_LONGEST_LENGTH_GT: Int - role_LONGEST_LENGTH_GTE: Int - role_LONGEST_LENGTH_LT: Int - role_LONGEST_LENGTH_LTE: Int - role_SHORTEST_LENGTH_EQUAL: Int - role_SHORTEST_LENGTH_GT: Int - role_SHORTEST_LENGTH_GTE: Int - role_SHORTEST_LENGTH_LT: Int - role_SHORTEST_LENGTH_LTE: Int + role: StringScalarAggregationFilters + role_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { eq: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gte: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lte: ... } } }' instead.\\") + role_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { eq: ... } } }' instead.\\") + role_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gt: ... } } }' instead.\\") + role_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gte: ... } } }' instead.\\") + role_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lt: ... } } }' instead.\\") + role_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { eq: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -878,26 +993,26 @@ describe("Directive-preserve", () => { } input ActedInUpdateInput { - role: String @deprecated(reason: \\"Please use the explicit _SET field\\") - role_SET: String + role: StringScalarMutations + role_SET: String @deprecated(reason: \\"Please use the generic mutation 'role: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - role: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - role_CONTAINS: String - role_ENDS_WITH: String - role_EQ: String - role_IN: [String!] - role_STARTS_WITH: String + role: StringScalarFilters + role_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter role: { contains: ... }\\") + role_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { endsWith: ... }\\") + role_EQ: String @deprecated(reason: \\"Please use the relevant generic filter role: { eq: ... }\\") + role_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter role: { in: ... }\\") + role_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { startsWith: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -905,7 +1020,7 @@ describe("Directive-preserve", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -927,6 +1042,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -964,21 +1098,22 @@ describe("Directive-preserve", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -1032,15 +1167,6 @@ describe("Directive-preserve", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -1055,6 +1181,17 @@ describe("Directive-preserve", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1064,45 +1201,47 @@ describe("Directive-preserve", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1142,6 +1281,16 @@ describe("Directive-preserve", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -1149,10 +1298,27 @@ describe("Directive-preserve", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! @deprecated(reason: \\"Do not use\\") - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection @deprecated(reason: \\"Do not use\\") - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! @deprecated(reason: \\"Do not use\\") + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! @deprecated(reason: \\"Do not use\\") + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection @deprecated(reason: \\"Do not use\\") + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! @deprecated(reason: \\"Do not use\\") runtime: Int! title: String! } @@ -1175,7 +1341,7 @@ describe("Directive-preserve", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1188,11 +1354,26 @@ describe("Directive-preserve", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere @deprecated(reason: \\"Do not use\\") } input MovieActorsCreateFieldInput { @@ -1209,21 +1390,22 @@ describe("Directive-preserve", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -1261,15 +1443,6 @@ describe("Directive-preserve", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1280,19 +1453,21 @@ describe("Directive-preserve", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] @deprecated(reason: \\"Do not use\\") - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput @deprecated(reason: \\"Do not use\\") + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" @@ -1317,19 +1492,19 @@ describe("Directive-preserve", () => { actors_SINGLE: ActorWhere @deprecated(reason: \\"Do not use\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" actors_SOME: ActorWhere @deprecated(reason: \\"Do not use\\") - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1359,7 +1534,7 @@ describe("Directive-preserve", () => { } interface Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! title: String! } @@ -1368,7 +1543,7 @@ describe("Directive-preserve", () => { AND: [ProductionActorsAggregateInput!] NOT: ProductionActorsAggregateInput OR: [ProductionActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1381,10 +1556,6 @@ describe("Directive-preserve", () => { input ProductionActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ProductionActorsEdgeCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1394,6 +1565,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input ProductionActorsConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionActorsConnections match this filter + \\"\\"\\" + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere + } + input ProductionActorsConnectionSort { edge: ProductionActorsEdgeSort node: ActorSort @@ -1471,21 +1661,22 @@ describe("Directive-preserve", () => { AND: [ProductionActorsNodeAggregationWhereInput!] NOT: ProductionActorsNodeAggregationWhereInput OR: [ProductionActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ProductionActorsRelationship { @@ -1546,13 +1737,15 @@ describe("Directive-preserve", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -1564,47 +1757,48 @@ describe("Directive-preserve", () => { input ProductionUpdateInput { actors: [ProductionActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + actors: ActorRelationshipFilters actorsAggregate: ProductionActorsAggregateInput + actorsConnection: ProductionActorsConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Productions where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Productions where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Productions where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Productions where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -1614,24 +1808,24 @@ describe("Directive-preserve", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! episodes: Int! title: String! } @@ -1654,7 +1848,7 @@ describe("Directive-preserve", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1667,11 +1861,26 @@ describe("Directive-preserve", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -1688,21 +1897,22 @@ describe("Directive-preserve", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -1746,15 +1956,6 @@ describe("Directive-preserve", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -1765,56 +1966,58 @@ describe("Directive-preserve", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -1830,6 +2033,27 @@ describe("Directive-preserve", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1920,21 +2144,22 @@ describe("Directive-preserve", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - role_AVERAGE_LENGTH_EQUAL: Float - role_AVERAGE_LENGTH_GT: Float - role_AVERAGE_LENGTH_GTE: Float - role_AVERAGE_LENGTH_LT: Float - role_AVERAGE_LENGTH_LTE: Float - role_LONGEST_LENGTH_EQUAL: Int - role_LONGEST_LENGTH_GT: Int - role_LONGEST_LENGTH_GTE: Int - role_LONGEST_LENGTH_LT: Int - role_LONGEST_LENGTH_LTE: Int - role_SHORTEST_LENGTH_EQUAL: Int - role_SHORTEST_LENGTH_GT: Int - role_SHORTEST_LENGTH_GTE: Int - role_SHORTEST_LENGTH_LT: Int - role_SHORTEST_LENGTH_LTE: Int + role: StringScalarAggregationFilters + role_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { eq: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gte: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lte: ... } } }' instead.\\") + role_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { eq: ... } } }' instead.\\") + role_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gt: ... } } }' instead.\\") + role_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gte: ... } } }' instead.\\") + role_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lt: ... } } }' instead.\\") + role_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { eq: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -1946,26 +2171,26 @@ describe("Directive-preserve", () => { } input ActedInUpdateInput { - role: String @deprecated(reason: \\"Please use the explicit _SET field\\") - role_SET: String + role: StringScalarMutations + role_SET: String @deprecated(reason: \\"Please use the generic mutation 'role: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - role: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - role_CONTAINS: String - role_ENDS_WITH: String - role_EQ: String - role_IN: [String!] - role_STARTS_WITH: String + role: StringScalarFilters + role_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter role: { contains: ... }\\") + role_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { endsWith: ... }\\") + role_EQ: String @deprecated(reason: \\"Please use the relevant generic filter role: { eq: ... }\\") + role_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter role: { in: ... }\\") + role_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { startsWith: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -1973,7 +2198,7 @@ describe("Directive-preserve", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1994,6 +2219,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -2029,21 +2273,22 @@ describe("Directive-preserve", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -2097,15 +2342,6 @@ describe("Directive-preserve", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -2120,6 +2356,17 @@ describe("Directive-preserve", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere @deprecated(reason: \\"Do not use\\") + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -2129,45 +2376,47 @@ describe("Directive-preserve", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2207,6 +2456,16 @@ describe("Directive-preserve", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -2214,10 +2473,27 @@ describe("Directive-preserve", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! @deprecated(reason: \\"Do not use\\") - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection @deprecated(reason: \\"Do not use\\") - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! @deprecated(reason: \\"Do not use\\") + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! @deprecated(reason: \\"Do not use\\") + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection @deprecated(reason: \\"Do not use\\") + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! @deprecated(reason: \\"Do not use\\") runtime: Int! title: String! } @@ -2240,7 +2516,7 @@ describe("Directive-preserve", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2253,10 +2529,6 @@ describe("Directive-preserve", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2266,6 +2538,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere @deprecated(reason: \\"Do not use\\") + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -2303,21 +2594,22 @@ describe("Directive-preserve", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2361,15 +2653,6 @@ describe("Directive-preserve", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2380,19 +2663,21 @@ describe("Directive-preserve", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] @deprecated(reason: \\"Do not use\\") - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput @deprecated(reason: \\"Do not use\\") + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" @@ -2417,19 +2702,19 @@ describe("Directive-preserve", () => { actors_SINGLE: ActorWhere @deprecated(reason: \\"Do not use\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" actors_SOME: ActorWhere @deprecated(reason: \\"Do not use\\") - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2487,13 +2772,15 @@ describe("Directive-preserve", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -2504,22 +2791,21 @@ describe("Directive-preserve", () => { } input ProductionUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -2529,24 +2815,24 @@ describe("Directive-preserve", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesActorsConnectionSort!], where: SeriesActorsConnectionWhere): SeriesActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [SeriesActorsConnectionSort!], where: SeriesActorsConnectionWhere): SeriesActorsConnection! episodes: Int! title: String! } @@ -2569,7 +2855,7 @@ describe("Directive-preserve", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2582,10 +2868,6 @@ describe("Directive-preserve", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2595,6 +2877,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input SeriesActorsConnectionFilters { + \\"\\"\\" + Return Series where all of the related SeriesActorsConnections match this filter + \\"\\"\\" + all: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related SeriesActorsConnections match this filter + \\"\\"\\" + none: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related SeriesActorsConnections match this filter + \\"\\"\\" + single: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related SeriesActorsConnections match this filter + \\"\\"\\" + some: SeriesActorsConnectionWhere + } + input SeriesActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -2632,21 +2933,22 @@ describe("Directive-preserve", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type SeriesActorsRelationship { @@ -2696,15 +2998,6 @@ describe("Directive-preserve", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -2715,56 +3008,58 @@ describe("Directive-preserve", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: SeriesActorsConnectionWhere + actorsConnection_ALL: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: SeriesActorsConnectionWhere + actorsConnection_NONE: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: SeriesActorsConnectionWhere + actorsConnection_SINGLE: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: SeriesActorsConnectionWhere + actorsConnection_SOME: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -2780,6 +3075,27 @@ describe("Directive-preserve", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2858,21 +3174,22 @@ describe("Directive-preserve", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - role_AVERAGE_LENGTH_EQUAL: Float - role_AVERAGE_LENGTH_GT: Float - role_AVERAGE_LENGTH_GTE: Float - role_AVERAGE_LENGTH_LT: Float - role_AVERAGE_LENGTH_LTE: Float - role_LONGEST_LENGTH_EQUAL: Int - role_LONGEST_LENGTH_GT: Int - role_LONGEST_LENGTH_GTE: Int - role_LONGEST_LENGTH_LT: Int - role_LONGEST_LENGTH_LTE: Int - role_SHORTEST_LENGTH_EQUAL: Int - role_SHORTEST_LENGTH_GT: Int - role_SHORTEST_LENGTH_GTE: Int - role_SHORTEST_LENGTH_LT: Int - role_SHORTEST_LENGTH_LTE: Int + role: StringScalarAggregationFilters + role_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { eq: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { gte: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lt: ... } } }' instead.\\") + role_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'role: { averageLength: { lte: ... } } }' instead.\\") + role_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { eq: ... } } }' instead.\\") + role_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gt: ... } } }' instead.\\") + role_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { gte: ... } } }' instead.\\") + role_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lt: ... } } }' instead.\\") + role_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { longestLength: { lte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { eq: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { gte: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lt: ... } } }' instead.\\") + role_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'role: { shortestLength: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -2884,26 +3201,26 @@ describe("Directive-preserve", () => { } input ActedInUpdateInput { - role: String @deprecated(reason: \\"Please use the explicit _SET field\\") - role_SET: String + role: StringScalarMutations + role_SET: String @deprecated(reason: \\"Please use the generic mutation 'role: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - role: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - role_CONTAINS: String - role_ENDS_WITH: String - role_EQ: String - role_IN: [String!] - role_STARTS_WITH: String + role: StringScalarFilters + role_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter role: { contains: ... }\\") + role_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { endsWith: ... }\\") + role_EQ: String @deprecated(reason: \\"Please use the relevant generic filter role: { eq: ... }\\") + role_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter role: { in: ... }\\") + role_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter role: { startsWith: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -2911,7 +3228,7 @@ describe("Directive-preserve", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2932,6 +3249,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -2967,21 +3303,22 @@ describe("Directive-preserve", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -3035,15 +3372,6 @@ describe("Directive-preserve", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -3058,6 +3386,17 @@ describe("Directive-preserve", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -3067,45 +3406,47 @@ describe("Directive-preserve", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -3145,6 +3486,16 @@ describe("Directive-preserve", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -3152,10 +3503,27 @@ describe("Directive-preserve", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! runtime: Int! title: String! } @@ -3178,7 +3546,7 @@ describe("Directive-preserve", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3191,10 +3559,6 @@ describe("Directive-preserve", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3204,6 +3568,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -3241,21 +3624,22 @@ describe("Directive-preserve", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -3299,15 +3683,6 @@ describe("Directive-preserve", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3318,56 +3693,58 @@ describe("Directive-preserve", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -3425,13 +3802,15 @@ describe("Directive-preserve", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -3442,22 +3821,21 @@ describe("Directive-preserve", () => { } input ProductionUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -3467,24 +3845,24 @@ describe("Directive-preserve", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesActorsConnectionSort!], where: SeriesActorsConnectionWhere): SeriesActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [SeriesActorsConnectionSort!], where: SeriesActorsConnectionWhere): SeriesActorsConnection! episodes: Int! title: String! } @@ -3507,7 +3885,7 @@ describe("Directive-preserve", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3520,10 +3898,6 @@ describe("Directive-preserve", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3533,6 +3907,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input SeriesActorsConnectionFilters { + \\"\\"\\" + Return Series where all of the related SeriesActorsConnections match this filter + \\"\\"\\" + all: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related SeriesActorsConnections match this filter + \\"\\"\\" + none: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related SeriesActorsConnections match this filter + \\"\\"\\" + single: SeriesActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related SeriesActorsConnections match this filter + \\"\\"\\" + some: SeriesActorsConnectionWhere + } + input SeriesActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -3570,21 +3963,22 @@ describe("Directive-preserve", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type SeriesActorsRelationship { @@ -3634,15 +4028,6 @@ describe("Directive-preserve", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -3653,56 +4038,58 @@ describe("Directive-preserve", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: SeriesActorsConnectionWhere + actorsConnection_ALL: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: SeriesActorsConnectionWhere + actorsConnection_NONE: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: SeriesActorsConnectionWhere + actorsConnection_SINGLE: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related SeriesActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: SeriesActorsConnectionWhere + actorsConnection_SOME: SeriesActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -3718,6 +4105,27 @@ describe("Directive-preserve", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -3776,9 +4184,9 @@ describe("Directive-preserve", () => { } type Blog { - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): BlogPostPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [BlogPostsConnectionSort!], where: BlogPostsConnectionWhere): BlogPostsConnection! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postsAggregate(where: PostWhere): BlogPostPostsAggregationSelection + postsConnection(after: String, first: Int, sort: [BlogPostsConnectionSort!], where: BlogPostsConnectionWhere): BlogPostsConnection! title: String } @@ -3813,15 +4221,6 @@ describe("Directive-preserve", () => { node: Blog! } - input BlogOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BlogSort objects to sort Blogs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BlogSort!] - } - type BlogPostPostsAggregationSelection { count: Int! node: BlogPostPostsNodeAggregateSelection @@ -3835,7 +4234,7 @@ describe("Directive-preserve", () => { AND: [BlogPostsAggregateInput!] NOT: BlogPostsAggregateInput OR: [BlogPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3845,10 +4244,6 @@ describe("Directive-preserve", () => { } input BlogPostsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PostConnectWhere } @@ -3858,6 +4253,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input BlogPostsConnectionFilters { + \\"\\"\\" + Return Blogs where all of the related BlogPostsConnections match this filter + \\"\\"\\" + all: BlogPostsConnectionWhere + \\"\\"\\" + Return Blogs where none of the related BlogPostsConnections match this filter + \\"\\"\\" + none: BlogPostsConnectionWhere + \\"\\"\\" + Return Blogs where one of the related BlogPostsConnections match this filter + \\"\\"\\" + single: BlogPostsConnectionWhere + \\"\\"\\" + Return Blogs where some of the related BlogPostsConnections match this filter + \\"\\"\\" + some: BlogPostsConnectionWhere + } + input BlogPostsConnectionSort { node: PostSort } @@ -3890,21 +4304,22 @@ describe("Directive-preserve", () => { AND: [BlogPostsNodeAggregationWhereInput!] NOT: BlogPostsNodeAggregationWhereInput OR: [BlogPostsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Do not use post.content\\") - content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Do not use post.content\\") - content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Do not use post.content\\") - content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Do not use post.content\\") - content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Do not use post.content\\") - content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Do not use post.content\\") - content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Do not use post.content\\") - content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Do not use post.content\\") - content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Do not use post.content\\") - content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Do not use post.content\\") - content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Do not use post.content\\") - content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Do not use post.content\\") - content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Do not use post.content\\") - content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Do not use post.content\\") - content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Do not use post.content\\") + content: StringScalarAggregationFilters + content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { eq: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gte: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lte: ... } } }' instead.\\") + content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { eq: ... } } }' instead.\\") + content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gt: ... } } }' instead.\\") + content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gte: ... } } }' instead.\\") + content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lt: ... } } }' instead.\\") + content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { eq: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lte: ... } } }' instead.\\") } type BlogPostsRelationship { @@ -3934,45 +4349,47 @@ describe("Directive-preserve", () => { input BlogUpdateInput { posts: [BlogPostsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input BlogWhere { AND: [BlogWhere!] NOT: BlogWhere OR: [BlogWhere!] + posts: PostRelationshipFilters postsAggregate: BlogPostsAggregateInput + postsConnection: BlogPostsConnectionFilters \\"\\"\\" Return Blogs where all of the related BlogPostsConnections match this filter \\"\\"\\" - postsConnection_ALL: BlogPostsConnectionWhere + postsConnection_ALL: BlogPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Blogs where none of the related BlogPostsConnections match this filter \\"\\"\\" - postsConnection_NONE: BlogPostsConnectionWhere + postsConnection_NONE: BlogPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Blogs where one of the related BlogPostsConnections match this filter \\"\\"\\" - postsConnection_SINGLE: BlogPostsConnectionWhere + postsConnection_SINGLE: BlogPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Blogs where some of the related BlogPostsConnections match this filter \\"\\"\\" - postsConnection_SOME: BlogPostsConnectionWhere + postsConnection_SOME: BlogPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Blogs where all of the related Posts match this filter\\"\\"\\" - posts_ALL: PostWhere + posts_ALL: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { all: ... }' instead.\\") \\"\\"\\"Return Blogs where none of the related Posts match this filter\\"\\"\\" - posts_NONE: PostWhere + posts_NONE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { none: ... }' instead.\\") \\"\\"\\"Return Blogs where one of the related Posts match this filter\\"\\"\\" - posts_SINGLE: PostWhere + posts_SINGLE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { single: ... }' instead.\\") \\"\\"\\"Return Blogs where some of the related Posts match this filter\\"\\"\\" - posts_SOME: PostWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + posts_SOME: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type BlogsConnection { @@ -3983,6 +4400,17 @@ describe("Directive-preserve", () => { union Content = Blog | Post + input ContentRelationshipFilters { + \\"\\"\\"Filter type where all of the related Contents match this filter\\"\\"\\" + all: ContentWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\"Filter type where none of the related Contents match this filter\\"\\"\\" + none: ContentWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\"Filter type where one of the related Contents match this filter\\"\\"\\" + single: ContentWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\"Filter type where some of the related Contents match this filter\\"\\"\\" + some: ContentWhere @deprecated(reason: \\"Do not use user.content\\") + } + input ContentWhere { Blog: BlogWhere Post: PostWhere @@ -4019,6 +4447,26 @@ describe("Directive-preserve", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createBlogs(input: [BlogCreateInput!]!): CreateBlogsMutationResponse! createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! @@ -4061,13 +4509,15 @@ describe("Directive-preserve", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] + input PostRelationshipFilters { + \\"\\"\\"Filter type where all of the related Posts match this filter\\"\\"\\" + all: PostWhere + \\"\\"\\"Filter type where none of the related Posts match this filter\\"\\"\\" + none: PostWhere + \\"\\"\\"Filter type where one of the related Posts match this filter\\"\\"\\" + single: PostWhere + \\"\\"\\"Filter type where some of the related Posts match this filter\\"\\"\\" + some: PostWhere } \\"\\"\\" @@ -4078,7 +4528,7 @@ describe("Directive-preserve", () => { } input PostUpdateInput { - content: String @deprecated(reason: \\"Do not use post.content\\") + content: StringScalarMutations @deprecated(reason: \\"Do not use post.content\\") content_SET: String @deprecated(reason: \\"Do not use post.content\\") } @@ -4086,7 +4536,7 @@ describe("Directive-preserve", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] - content: String @deprecated(reason: \\"Do not use post.content\\") + content: StringScalarFilters @deprecated(reason: \\"Do not use post.content\\") content_CONTAINS: String @deprecated(reason: \\"Do not use post.content\\") content_ENDS_WITH: String @deprecated(reason: \\"Do not use post.content\\") content_EQ: String @deprecated(reason: \\"Do not use post.content\\") @@ -4101,24 +4551,18 @@ describe("Directive-preserve", () => { } type Query { - blogs(limit: Int, offset: Int, options: BlogOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BlogSort!], where: BlogWhere): [Blog!]! + blogs(limit: Int, offset: Int, sort: [BlogSort!], where: BlogWhere): [Blog!]! blogsAggregate(where: BlogWhere): BlogAggregateSelection! blogsConnection(after: String, first: Int, sort: [BlogSort!], where: BlogWhere): BlogsConnection! - contents(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ContentWhere): [Content!]! - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + contents(limit: Int, offset: Int, where: ContentWhere): [Content!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -4132,6 +4576,27 @@ describe("Directive-preserve", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateBlogsMutationResponse { blogs: [Blog!]! info: UpdateInfo! @@ -4158,8 +4623,8 @@ describe("Directive-preserve", () => { } type User { - content(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ContentWhere): [Content!]! @deprecated(reason: \\"Do not use user.content\\") - contentConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: UserContentConnectionWhere): UserContentConnection! @deprecated(reason: \\"Do not use user.content\\") + content(limit: Int, offset: Int, where: ContentWhere): [Content!]! @deprecated(reason: \\"Do not use user.content\\") + contentConnection(after: String, first: Int, where: UserContentConnectionWhere): UserContentConnection! @deprecated(reason: \\"Do not use user.content\\") name: String } @@ -4218,6 +4683,25 @@ describe("Directive-preserve", () => { totalCount: Int! } + input UserContentConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserContentConnections match this filter + \\"\\"\\" + all: UserContentConnectionWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\" + Return Users where none of the related UserContentConnections match this filter + \\"\\"\\" + none: UserContentConnectionWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\" + Return Users where one of the related UserContentConnections match this filter + \\"\\"\\" + single: UserContentConnectionWhere @deprecated(reason: \\"Do not use user.content\\") + \\"\\"\\" + Return Users where some of the related UserContentConnections match this filter + \\"\\"\\" + some: UserContentConnectionWhere @deprecated(reason: \\"Do not use user.content\\") + } + input UserContentConnectionWhere { Blog: UserContentBlogConnectionWhere Post: UserContentPostConnectionWhere @@ -4298,15 +4782,6 @@ describe("Directive-preserve", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - \\"\\"\\" Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. \\"\\"\\" @@ -4316,14 +4791,16 @@ describe("Directive-preserve", () => { input UserUpdateInput { content: UserContentUpdateInput @deprecated(reason: \\"Do not use user.content\\") - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] + content: ContentRelationshipFilters + contentConnection: UserContentConnectionFilters \\"\\"\\" Return Users where all of the related UserContentConnections match this filter \\"\\"\\" @@ -4348,12 +4825,12 @@ describe("Directive-preserve", () => { content_SINGLE: ContentWhere @deprecated(reason: \\"Do not use user.content\\") \\"\\"\\"Return Users where some of the related Contents match this filter\\"\\"\\" content_SOME: ContentWhere @deprecated(reason: \\"Do not use user.content\\") - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/directives/alias.test.ts b/packages/graphql/tests/schema/directives/alias.test.ts index f655871bae..6e625b7a97 100644 --- a/packages/graphql/tests/schema/directives/alias.test.ts +++ b/packages/graphql/tests/schema/directives/alias.test.ts @@ -51,9 +51,9 @@ describe("Alias", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! city: String name: String! } @@ -62,7 +62,7 @@ describe("Alias", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -74,10 +74,6 @@ describe("Alias", () => { input ActorActedInConnectFieldInput { edge: ActorActedInPropsCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -87,6 +83,25 @@ describe("Alias", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActorActedInPropsSort node: MovieSort @@ -122,41 +137,43 @@ describe("Alias", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - rating_AVERAGE_EQUAL: Float - rating_AVERAGE_GT: Float - rating_AVERAGE_GTE: Float - rating_AVERAGE_LT: Float - rating_AVERAGE_LTE: Float - rating_MAX_EQUAL: Float - rating_MAX_GT: Float - rating_MAX_GTE: Float - rating_MAX_LT: Float - rating_MAX_LTE: Float - rating_MIN_EQUAL: Float - rating_MIN_GT: Float - rating_MIN_GTE: Float - rating_MIN_LT: Float - rating_MIN_LTE: Float - rating_SUM_EQUAL: Float - rating_SUM_GT: Float - rating_SUM_GTE: Float - rating_SUM_LT: Float - rating_SUM_LTE: Float - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + rating: FloatScalarAggregationFilters + rating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { average: { eq: ... } } }' instead.\\") + rating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { average: { gt: ... } } }' instead.\\") + rating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { average: { gte: ... } } }' instead.\\") + rating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { average: { lt: ... } } }' instead.\\") + rating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { average: { lte: ... } } }' instead.\\") + rating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { max: { eq: ... } } }' instead.\\") + rating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { max: { gt: ... } } }' instead.\\") + rating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { max: { gte: ... } } }' instead.\\") + rating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { max: { lt: ... } } }' instead.\\") + rating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { max: { lte: ... } } }' instead.\\") + rating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { min: { eq: ... } } }' instead.\\") + rating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { min: { gt: ... } } }' instead.\\") + rating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { min: { gte: ... } } }' instead.\\") + rating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { min: { lt: ... } } }' instead.\\") + rating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { min: { lte: ... } } }' instead.\\") + rating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { sum: { eq: ... } } }' instead.\\") + rating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { sum: { gt: ... } } }' instead.\\") + rating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { sum: { gte: ... } } }' instead.\\") + rating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { sum: { lt: ... } } }' instead.\\") + rating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'rating: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } \\"\\"\\" @@ -172,41 +189,43 @@ describe("Alias", () => { AND: [ActorActedInPropsAggregationWhereInput!] NOT: ActorActedInPropsAggregationWhereInput OR: [ActorActedInPropsAggregationWhereInput!] - character_AVERAGE_LENGTH_EQUAL: Float - character_AVERAGE_LENGTH_GT: Float - character_AVERAGE_LENGTH_GTE: Float - character_AVERAGE_LENGTH_LT: Float - character_AVERAGE_LENGTH_LTE: Float - character_LONGEST_LENGTH_EQUAL: Int - character_LONGEST_LENGTH_GT: Int - character_LONGEST_LENGTH_GTE: Int - character_LONGEST_LENGTH_LT: Int - character_LONGEST_LENGTH_LTE: Int - character_SHORTEST_LENGTH_EQUAL: Int - character_SHORTEST_LENGTH_GT: Int - character_SHORTEST_LENGTH_GTE: Int - character_SHORTEST_LENGTH_LT: Int - character_SHORTEST_LENGTH_LTE: Int - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + character: StringScalarAggregationFilters + character_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'character: { averageLength: { eq: ... } } }' instead.\\") + character_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'character: { averageLength: { gt: ... } } }' instead.\\") + character_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'character: { averageLength: { gte: ... } } }' instead.\\") + character_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'character: { averageLength: { lt: ... } } }' instead.\\") + character_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'character: { averageLength: { lte: ... } } }' instead.\\") + character_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { longestLength: { eq: ... } } }' instead.\\") + character_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { longestLength: { gt: ... } } }' instead.\\") + character_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { longestLength: { gte: ... } } }' instead.\\") + character_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { longestLength: { lt: ... } } }' instead.\\") + character_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { longestLength: { lte: ... } } }' instead.\\") + character_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { shortestLength: { eq: ... } } }' instead.\\") + character_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { shortestLength: { gt: ... } } }' instead.\\") + character_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { shortestLength: { gte: ... } } }' instead.\\") + character_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { shortestLength: { lt: ... } } }' instead.\\") + character_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'character: { shortestLength: { lte: ... } } }' instead.\\") + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActorActedInPropsCreateInput { @@ -220,31 +239,31 @@ describe("Alias", () => { } input ActorActedInPropsUpdateInput { - character: String @deprecated(reason: \\"Please use the explicit _SET field\\") - character_SET: String - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + character: StringScalarMutations + character_SET: String @deprecated(reason: \\"Please use the generic mutation 'character: { set: ... } }' instead.\\") + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActorActedInPropsWhere { AND: [ActorActedInPropsWhere!] NOT: ActorActedInPropsWhere OR: [ActorActedInPropsWhere!] - character: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - character_CONTAINS: String - character_ENDS_WITH: String - character_EQ: String - character_IN: [String!] - character_STARTS_WITH: String - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int] - screenTime_LT: Int - screenTime_LTE: Int + character: StringScalarFilters + character_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter character: { contains: ... }\\") + character_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter character: { endsWith: ... }\\") + character_EQ: String @deprecated(reason: \\"Please use the relevant generic filter character: { eq: ... }\\") + character_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter character: { in: ... }\\") + character_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter character: { startsWith: ... }\\") + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type ActorActedInRelationship { @@ -304,15 +323,6 @@ describe("Alias", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -323,53 +333,55 @@ describe("Alias", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - city: String @deprecated(reason: \\"Please use the explicit _SET field\\") - city_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + city: StringScalarMutations + city_SET: String @deprecated(reason: \\"Please use the generic mutation 'city: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - city: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - city_CONTAINS: String - city_ENDS_WITH: String - city_EQ: String - city_IN: [String] - city_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + city: StringScalarFilters + city_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter city: { contains: ... }\\") + city_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter city: { endsWith: ... }\\") + city_EQ: String @deprecated(reason: \\"Please use the relevant generic filter city: { eq: ... }\\") + city_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter city: { in: ... }\\") + city_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter city: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -411,6 +423,33 @@ describe("Alias", () => { sum: Float } + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + type IntAggregateSelection { average: Float max: Int @@ -418,6 +457,31 @@ describe("Alias", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { rating: Float title: String! @@ -443,13 +507,15 @@ describe("Alias", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -461,33 +527,33 @@ describe("Alias", () => { } input MovieUpdateInput { - rating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - rating_ADD: Float - rating_DIVIDE: Float - rating_MULTIPLY: Float - rating_SET: Float - rating_SUBTRACT: Float - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + rating: FloatScalarMutations + rating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { add: ... } }' instead.\\") + rating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { divide: ... } }' instead.\\") + rating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { multiply: ... } }' instead.\\") + rating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'rating: { set: ... } }' instead.\\") + rating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { subtract: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - rating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - rating_EQ: Float - rating_GT: Float - rating_GTE: Float - rating_IN: [Float] - rating_LT: Float - rating_LTE: Float - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + rating: FloatScalarFilters + rating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { eq: ... }\\") + rating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { gt: ... }\\") + rating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { gte: ... }\\") + rating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter rating: { in: ... }\\") + rating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { lt: ... }\\") + rating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -514,10 +580,10 @@ describe("Alias", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -535,6 +601,27 @@ describe("Alias", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/autogenerate.test.ts b/packages/graphql/tests/schema/directives/autogenerate.test.ts index 8e2ea3b380..bd14e3c65f 100644 --- a/packages/graphql/tests/schema/directives/autogenerate.test.ts +++ b/packages/graphql/tests/schema/directives/autogenerate.test.ts @@ -26,7 +26,7 @@ describe("Autogenerate", () => { test("Simple", async () => { const typeDefs = gql` type Movie @node { - id: ID! @id @unique + id: ID! @id name: String! } `; @@ -60,9 +60,13 @@ describe("Autogenerate", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID } type Movie { @@ -72,7 +76,6 @@ describe("Autogenerate", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -85,15 +88,6 @@ describe("Autogenerate", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -103,26 +97,26 @@ describe("Autogenerate", () => { } input MovieUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type MoviesConnection { @@ -146,7 +140,7 @@ describe("Autogenerate", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -164,6 +158,20 @@ describe("Autogenerate", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/directives/customResolver.test.ts b/packages/graphql/tests/schema/directives/customResolver.test.ts index 5c6cf58cf3..06f71e5a86 100644 --- a/packages/graphql/tests/schema/directives/customResolver.test.ts +++ b/packages/graphql/tests/schema/directives/customResolver.test.ts @@ -75,9 +75,18 @@ describe("@customResolver directive", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Mutation { @@ -95,10 +104,10 @@ describe("@customResolver directive", () => { } type Query { - userInterfaces(limit: Int, offset: Int, options: UserInterfaceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! + userInterfaces(limit: Int, offset: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! userInterfacesAggregate(where: UserInterfaceWhere): UserInterfaceAggregateSelection! userInterfacesConnection(after: String, first: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): UserInterfacesConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -116,6 +125,20 @@ describe("@customResolver directive", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -141,7 +164,6 @@ describe("@customResolver directive", () => { type UserAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") password: StringAggregateSelection! username: StringAggregateSelection! } @@ -175,15 +197,6 @@ describe("@customResolver directive", () => { User } - input UserInterfaceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserInterfaceSort objects to sort UserInterfaces by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserInterfaceSort!] - } - \\"\\"\\" Fields to sort UserInterfaces by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserInterfaceSort object. \\"\\"\\" @@ -195,14 +208,13 @@ describe("@customResolver directive", () => { AND: [UserInterfaceWhere!] NOT: UserInterfaceWhere OR: [UserInterfaceWhere!] - customResolver: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - customResolver_CONTAINS: String - customResolver_ENDS_WITH: String - customResolver_EQ: String - customResolver_IN: [String] - customResolver_STARTS_WITH: String + customResolver: StringScalarFilters + customResolver_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter customResolver: { contains: ... }\\") + customResolver_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter customResolver: { endsWith: ... }\\") + customResolver_EQ: String @deprecated(reason: \\"Please use the relevant generic filter customResolver: { eq: ... }\\") + customResolver_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter customResolver: { in: ... }\\") + customResolver_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter customResolver: { startsWith: ... }\\") typename: [UserInterfaceImplementation!] - typename_IN: [UserInterfaceImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type UserInterfacesConnection { @@ -211,15 +223,6 @@ describe("@customResolver directive", () => { totalCount: Int! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - \\"\\"\\" Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. \\"\\"\\" @@ -230,36 +233,36 @@ describe("@customResolver directive", () => { } input UserUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/directives/cypher.test.ts b/packages/graphql/tests/schema/directives/cypher.test.ts index 26b8ec04ca..98947a7a0a 100644 --- a/packages/graphql/tests/schema/directives/cypher.test.ts +++ b/packages/graphql/tests/schema/directives/cypher.test.ts @@ -112,6 +112,15 @@ describe("Cypher", () => { columnName: "list_of_durations" ) actor: Actor @cypher(statement: "MATCH (this)-[:ACTED_IN]->(a:Actor) RETURN a", columnName: "a") + actors_no_args: [Actor] + @cypher( + statement: """ + MATCH (a:Actor {title: $title}) + RETURN a + LIMIT 1 + """ + columnName: "a" + ) actors(title: String): [Actor] @cypher( statement: """ @@ -150,13 +159,15 @@ describe("Cypher", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -167,20 +178,20 @@ describe("Cypher", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -194,6 +205,41 @@ describe("Cypher", () => { \\"\\"\\" scalar BigInt + \\"\\"\\"BigInt list filters\\"\\"\\" + input BigIntListFilters { + eq: [BigInt!] + includes: BigInt + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"Boolean list filters\\"\\"\\" + input BooleanListFilters { + eq: [Boolean!] + } + + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Distance filters for cartesian points\\"\\"\\" + input CartesianDistancePointFilters { + from: CartesianPointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + \\"\\"\\" A point in a two- or three-dimensional Cartesian coordinate system or in a three-dimensional cylindrical coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#cartesian-point \\"\\"\\" @@ -211,6 +257,13 @@ describe("Cypher", () => { point: CartesianPointInput! } + \\"\\"\\"Cartesian Point filters\\"\\"\\" + input CartesianPointFilters { + distance: CartesianDistancePointFilters + eq: CartesianPointInput + in: [CartesianPointInput!] + } + \\"\\"\\"Input type for a cartesian point\\"\\"\\" input CartesianPointInput { x: Float! @@ -218,6 +271,12 @@ describe("Cypher", () => { z: Float } + \\"\\"\\"CartesianPoint list filters\\"\\"\\" + input CartesianPointListFilters { + eq: [CartesianPointInput!] + includes: CartesianPointInput + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -239,9 +298,41 @@ describe("Cypher", () => { \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" scalar Date + \\"\\"\\"Date list filters\\"\\"\\" + input DateListFilters { + eq: [Date!] + includes: Date + } + + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" scalar DateTime + \\"\\"\\"DateTime list filters\\"\\"\\" + input DateTimeListFilters { + eq: [DateTime!] + includes: DateTime + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -253,22 +344,118 @@ describe("Cypher", () => { \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" scalar Duration - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Duration list filters\\"\\"\\" + input DurationListFilters { + eq: [Duration!] + includes: Duration + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID list filters\\"\\"\\" + input IDListFilters { + eq: [ID!] + includes: ID + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" scalar LocalDateTime + \\"\\"\\"LocalDateTime list filters\\"\\"\\" + input LocalDateTimeListFilters { + eq: [LocalDateTime!] + includes: LocalDateTime + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + \\"\\"\\" A local time, represented as a time string without timezone information \\"\\"\\" scalar LocalTime + \\"\\"\\"LocalTime list filters\\"\\"\\" + input LocalTimeListFilters { + eq: [LocalTime!] + includes: LocalTime + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + type Movie { actor: Actor actors(title: String): [Actor] + actors_no_args: [Actor] custom_big_int: BigInt custom_boolean: Boolean custom_cartesian_point: CartesianPoint @@ -302,7 +489,6 @@ describe("Cypher", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -314,15 +500,6 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -345,8 +522,8 @@ describe("Cypher", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { @@ -354,146 +531,151 @@ describe("Cypher", () => { NOT: MovieWhere OR: [MovieWhere!] actor: ActorWhere - custom_big_int: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_big_int_EQ: BigInt - custom_big_int_GT: BigInt - custom_big_int_GTE: BigInt - custom_big_int_IN: [BigInt] - custom_big_int_LT: BigInt - custom_big_int_LTE: BigInt - custom_boolean: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_boolean_EQ: Boolean - custom_cartesian_point: CartesianPointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_cartesian_point_DISTANCE: CartesianPointDistance - custom_cartesian_point_EQ: CartesianPointInput - custom_cartesian_point_GT: CartesianPointDistance - custom_cartesian_point_GTE: CartesianPointDistance - custom_cartesian_point_IN: [CartesianPointInput] - custom_cartesian_point_LT: CartesianPointDistance - custom_cartesian_point_LTE: CartesianPointDistance - custom_date: Date @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_date_EQ: Date - custom_date_GT: Date - custom_date_GTE: Date - custom_date_IN: [Date] - custom_date_LT: Date - custom_date_LTE: Date - custom_datetime: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_datetime_EQ: DateTime - custom_datetime_GT: DateTime - custom_datetime_GTE: DateTime - custom_datetime_IN: [DateTime] - custom_datetime_LT: DateTime - custom_datetime_LTE: DateTime - custom_duration: Duration @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_duration_EQ: Duration - custom_duration_GT: Duration - custom_duration_GTE: Duration - custom_duration_IN: [Duration] - custom_duration_LT: Duration - custom_duration_LTE: Duration - custom_float: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_float_EQ: Float - custom_float_GT: Float - custom_float_GTE: Float - custom_float_IN: [Float] - custom_float_LT: Float - custom_float_LTE: Float - custom_id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_id_CONTAINS: ID - custom_id_ENDS_WITH: ID - custom_id_EQ: ID - custom_id_IN: [ID] - custom_id_STARTS_WITH: ID - custom_int: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_int_EQ: Int - custom_int_GT: Int - custom_int_GTE: Int - custom_int_IN: [Int] - custom_int_LT: Int - custom_int_LTE: Int - custom_localdatetime: LocalDateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_localdatetime_EQ: LocalDateTime - custom_localdatetime_GT: LocalDateTime - custom_localdatetime_GTE: LocalDateTime - custom_localdatetime_IN: [LocalDateTime] - custom_localdatetime_LT: LocalDateTime - custom_localdatetime_LTE: LocalDateTime - custom_localtime: LocalTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_localtime_EQ: LocalTime - custom_localtime_GT: LocalTime - custom_localtime_GTE: LocalTime - custom_localtime_IN: [LocalTime] - custom_localtime_LT: LocalTime - custom_localtime_LTE: LocalTime - custom_point: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_point_DISTANCE: PointDistance - custom_point_EQ: PointInput - custom_point_GT: PointDistance - custom_point_GTE: PointDistance - custom_point_IN: [PointInput] - custom_point_LT: PointDistance - custom_point_LTE: PointDistance - custom_string: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_string_CONTAINS: String - custom_string_ENDS_WITH: String - custom_string_EQ: String - custom_string_IN: [String] - custom_string_STARTS_WITH: String - custom_time: Time @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_time_EQ: Time - custom_time_GT: Time - custom_time_GTE: Time - custom_time_IN: [Time] - custom_time_LT: Time - custom_time_LTE: Time - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - list_custom_of_ids: [ID] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_custom_of_ids_EQ: [ID] - list_custom_of_ids_INCLUDES: ID - list_of_custom_big_ints: [BigInt] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_big_ints_EQ: [BigInt] - list_of_custom_big_ints_INCLUDES: BigInt - list_of_custom_booleans: [Boolean] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_booleans_EQ: [Boolean] - list_of_custom_cartesian_points: [CartesianPointInput] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_cartesian_points_EQ: [CartesianPointInput] - list_of_custom_cartesian_points_INCLUDES: CartesianPointInput - list_of_custom_dates: [Date] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_dates_EQ: [Date] - list_of_custom_dates_INCLUDES: Date - list_of_custom_datetimes: [DateTime] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_datetimes_EQ: [DateTime] - list_of_custom_datetimes_INCLUDES: DateTime - list_of_custom_durations: [Duration] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_durations_EQ: [Duration] - list_of_custom_durations_INCLUDES: Duration - list_of_custom_floats: [Float] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_floats_EQ: [Float] - list_of_custom_floats_INCLUDES: Float - list_of_custom_ints: [Int] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_ints_EQ: [Int] - list_of_custom_ints_INCLUDES: Int - list_of_custom_localdatetimes: [LocalDateTime] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_localdatetimes_EQ: [LocalDateTime] - list_of_custom_localdatetimes_INCLUDES: LocalDateTime - list_of_custom_localtimes: [LocalTime] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_localtimes_EQ: [LocalTime] - list_of_custom_localtimes_INCLUDES: LocalTime - list_of_custom_points: [PointInput] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_points_EQ: [PointInput] - list_of_custom_points_INCLUDES: PointInput - list_of_custom_strings: [String] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_strings_EQ: [String] - list_of_custom_strings_INCLUDES: String - list_of_custom_times: [Time] @deprecated(reason: \\"Please use the explicit _EQ version\\") - list_of_custom_times_EQ: [Time] - list_of_custom_times_INCLUDES: Time + actors_no_args: ActorRelationshipFilters + actors_no_args_ALL: ActorWhere + actors_no_args_NONE: ActorWhere + actors_no_args_SINGLE: ActorWhere + actors_no_args_SOME: ActorWhere + custom_big_int: BigIntScalarFilters + custom_big_int_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { eq: ... }\\") + custom_big_int_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { gt: ... }\\") + custom_big_int_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { gte: ... }\\") + custom_big_int_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { in: ... }\\") + custom_big_int_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { lt: ... }\\") + custom_big_int_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter custom_big_int: { lte: ... }\\") + custom_boolean: BooleanScalarFilters + custom_boolean_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter custom_boolean: { eq: ... }\\") + custom_cartesian_point: CartesianPointFilters + custom_cartesian_point_DISTANCE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { distance: ... }\\") + custom_cartesian_point_EQ: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { eq: ... }\\") + custom_cartesian_point_GT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { gt: ... }\\") + custom_cartesian_point_GTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { gte: ... }\\") + custom_cartesian_point_IN: [CartesianPointInput] @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { in: ... }\\") + custom_cartesian_point_LT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { lt: ... }\\") + custom_cartesian_point_LTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_cartesian_point: { lte: ... }\\") + custom_date: DateScalarFilters + custom_date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter custom_date: { eq: ... }\\") + custom_date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter custom_date: { gt: ... }\\") + custom_date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter custom_date: { gte: ... }\\") + custom_date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter custom_date: { in: ... }\\") + custom_date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter custom_date: { lt: ... }\\") + custom_date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter custom_date: { lte: ... }\\") + custom_datetime: DateTimeScalarFilters + custom_datetime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { eq: ... }\\") + custom_datetime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { gt: ... }\\") + custom_datetime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { gte: ... }\\") + custom_datetime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { in: ... }\\") + custom_datetime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { lt: ... }\\") + custom_datetime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter custom_datetime: { lte: ... }\\") + custom_duration: DurationScalarFilters + custom_duration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { eq: ... }\\") + custom_duration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { gt: ... }\\") + custom_duration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { gte: ... }\\") + custom_duration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { in: ... }\\") + custom_duration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { lt: ... }\\") + custom_duration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter custom_duration: { lte: ... }\\") + custom_float: FloatScalarFilters + custom_float_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter custom_float: { eq: ... }\\") + custom_float_GT: Float @deprecated(reason: \\"Please use the relevant generic filter custom_float: { gt: ... }\\") + custom_float_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter custom_float: { gte: ... }\\") + custom_float_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter custom_float: { in: ... }\\") + custom_float_LT: Float @deprecated(reason: \\"Please use the relevant generic filter custom_float: { lt: ... }\\") + custom_float_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter custom_float: { lte: ... }\\") + custom_id: IDScalarFilters + custom_id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter custom_id: { contains: ... }\\") + custom_id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter custom_id: { endsWith: ... }\\") + custom_id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter custom_id: { eq: ... }\\") + custom_id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter custom_id: { in: ... }\\") + custom_id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter custom_id: { startsWith: ... }\\") + custom_int: IntScalarFilters + custom_int_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter custom_int: { eq: ... }\\") + custom_int_GT: Int @deprecated(reason: \\"Please use the relevant generic filter custom_int: { gt: ... }\\") + custom_int_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter custom_int: { gte: ... }\\") + custom_int_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter custom_int: { in: ... }\\") + custom_int_LT: Int @deprecated(reason: \\"Please use the relevant generic filter custom_int: { lt: ... }\\") + custom_int_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter custom_int: { lte: ... }\\") + custom_localdatetime: LocalDateTimeScalarFilters + custom_localdatetime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { eq: ... }\\") + custom_localdatetime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { gt: ... }\\") + custom_localdatetime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { gte: ... }\\") + custom_localdatetime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { in: ... }\\") + custom_localdatetime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { lt: ... }\\") + custom_localdatetime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter custom_localdatetime: { lte: ... }\\") + custom_localtime: LocalTimeScalarFilters + custom_localtime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { eq: ... }\\") + custom_localtime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { gt: ... }\\") + custom_localtime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { gte: ... }\\") + custom_localtime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { in: ... }\\") + custom_localtime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { lt: ... }\\") + custom_localtime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter custom_localtime: { lte: ... }\\") + custom_point: PointFilters + custom_point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_point: { distance: ... }\\") + custom_point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter custom_point: { eq: ... }\\") + custom_point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_point: { gt: ... }\\") + custom_point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_point: { gte: ... }\\") + custom_point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter custom_point: { in: ... }\\") + custom_point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_point: { lt: ... }\\") + custom_point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter custom_point: { lte: ... }\\") + custom_string: StringScalarFilters + custom_string_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter custom_string: { contains: ... }\\") + custom_string_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_string: { endsWith: ... }\\") + custom_string_EQ: String @deprecated(reason: \\"Please use the relevant generic filter custom_string: { eq: ... }\\") + custom_string_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter custom_string: { in: ... }\\") + custom_string_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_string: { startsWith: ... }\\") + custom_time: TimeScalarFilters + custom_time_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter custom_time: { eq: ... }\\") + custom_time_GT: Time @deprecated(reason: \\"Please use the relevant generic filter custom_time: { gt: ... }\\") + custom_time_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter custom_time: { gte: ... }\\") + custom_time_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter custom_time: { in: ... }\\") + custom_time_LT: Time @deprecated(reason: \\"Please use the relevant generic filter custom_time: { lt: ... }\\") + custom_time_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter custom_time: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + list_custom_of_ids: IDListFilters + list_custom_of_ids_EQ: [ID] @deprecated(reason: \\"Please use the relevant generic filter list_custom_of_ids: { eq: ... }\\") + list_custom_of_ids_INCLUDES: ID @deprecated(reason: \\"Please use the relevant generic filter list_custom_of_ids: { includes: ... }\\") + list_of_custom_big_ints: BigIntListFilters + list_of_custom_big_ints_EQ: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_big_ints: { eq: ... }\\") + list_of_custom_big_ints_INCLUDES: BigInt @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_big_ints: { includes: ... }\\") + list_of_custom_booleans: BooleanListFilters + list_of_custom_booleans_EQ: [Boolean] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_booleans: { eq: ... }\\") + list_of_custom_cartesian_points: CartesianPointListFilters + list_of_custom_cartesian_points_EQ: [CartesianPointInput] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_cartesian_points: { eq: ... }\\") + list_of_custom_cartesian_points_INCLUDES: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_cartesian_points: { includes: ... }\\") + list_of_custom_dates: DateListFilters + list_of_custom_dates_EQ: [Date] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_dates: { eq: ... }\\") + list_of_custom_dates_INCLUDES: Date @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_dates: { includes: ... }\\") + list_of_custom_datetimes: DateTimeListFilters + list_of_custom_datetimes_EQ: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_datetimes: { eq: ... }\\") + list_of_custom_datetimes_INCLUDES: DateTime @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_datetimes: { includes: ... }\\") + list_of_custom_durations: DurationListFilters + list_of_custom_durations_EQ: [Duration] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_durations: { eq: ... }\\") + list_of_custom_durations_INCLUDES: Duration @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_durations: { includes: ... }\\") + list_of_custom_floats: FloatListFilters + list_of_custom_floats_EQ: [Float] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_floats: { eq: ... }\\") + list_of_custom_floats_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_floats: { includes: ... }\\") + list_of_custom_ints: IntListFilters + list_of_custom_ints_EQ: [Int] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_ints: { eq: ... }\\") + list_of_custom_ints_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_ints: { includes: ... }\\") + list_of_custom_localdatetimes: LocalDateTimeListFilters + list_of_custom_localdatetimes_EQ: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_localdatetimes: { eq: ... }\\") + list_of_custom_localdatetimes_INCLUDES: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_localdatetimes: { includes: ... }\\") + list_of_custom_localtimes: LocalTimeListFilters + list_of_custom_localtimes_EQ: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_localtimes: { eq: ... }\\") + list_of_custom_localtimes_INCLUDES: LocalTime @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_localtimes: { includes: ... }\\") + list_of_custom_points: PointListFilters + list_of_custom_points_EQ: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_points: { eq: ... }\\") + list_of_custom_points_INCLUDES: PointInput @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_points: { includes: ... }\\") + list_of_custom_strings: StringListFilters + list_of_custom_strings_EQ: [String] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_strings: { eq: ... }\\") + list_of_custom_strings_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_strings: { includes: ... }\\") + list_of_custom_times: TimeListFilters + list_of_custom_times_EQ: [Time] @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_times: { eq: ... }\\") + list_of_custom_times_INCLUDES: Time @deprecated(reason: \\"Please use the relevant generic filter list_of_custom_times: { includes: ... }\\") } type MoviesConnection { @@ -537,6 +719,23 @@ describe("Cypher", () => { point: PointInput! } + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + \\"\\"\\"Input type for a point\\"\\"\\" input PointInput { height: Float @@ -544,11 +743,17 @@ describe("Cypher", () => { longitude: Float! } + \\"\\"\\"Point list filters\\"\\"\\" + input PointListFilters { + eq: [PointInput!] + includes: PointInput + } + type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -566,9 +771,45 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" scalar Time + \\"\\"\\"Time list filters\\"\\"\\" + input TimeListFilters { + eq: [Time!] + includes: Time + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -648,11 +889,6 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - } - input MovieUpdateInput { \\"\\"\\" Appears because this input type would be empty otherwise because this type is composed of just generated and/or relationship properties. See https://neo4j.com/docs/graphql-manual/current/troubleshooting/faqs/ @@ -664,9 +900,9 @@ describe("Cypher", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - custom_cypher_string_list: [String] @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_cypher_string_list_EQ: [String] - custom_cypher_string_list_INCLUDES: String + custom_cypher_string_list: StringListFilters + custom_cypher_string_list_EQ: [String] @deprecated(reason: \\"Please use the relevant generic filter custom_cypher_string_list: { eq: ... }\\") + custom_cypher_string_list_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter custom_cypher_string_list: { includes: ... }\\") } type MoviesConnection { @@ -690,11 +926,17 @@ describe("Cypher", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, where: MovieWhere): MoviesConnection! } + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -769,15 +1011,6 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -819,7 +1052,7 @@ describe("Cypher", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -908,15 +1141,6 @@ describe("Cypher", () => { node: Blog! } - input BlogOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BlogSort objects to sort Blogs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BlogSort!] - } - \\"\\"\\" Fields to sort Blogs by. The order in which sorts are applied is not guaranteed when specifying many fields in one BlogSort object. \\"\\"\\" @@ -925,8 +1149,8 @@ describe("Cypher", () => { } input BlogUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input BlogWhere { @@ -934,17 +1158,17 @@ describe("Cypher", () => { NOT: BlogWhere OR: [BlogWhere!] post: PostWhere - posts: PostWhere + posts: PostRelationshipFilters posts_ALL: PostWhere posts_NONE: PostWhere posts_SINGLE: PostWhere posts_SOME: PostWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type BlogsConnection { @@ -1021,13 +1245,15 @@ describe("Cypher", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] + input PostRelationshipFilters { + \\"\\"\\"Filter type where all of the related Posts match this filter\\"\\"\\" + all: PostWhere + \\"\\"\\"Filter type where none of the related Posts match this filter\\"\\"\\" + none: PostWhere + \\"\\"\\"Filter type where one of the related Posts match this filter\\"\\"\\" + single: PostWhere + \\"\\"\\"Filter type where some of the related Posts match this filter\\"\\"\\" + some: PostWhere } \\"\\"\\" @@ -1038,20 +1264,20 @@ describe("Cypher", () => { } input PostUpdateInput { - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") } input PostWhere { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String] - content_STARTS_WITH: String + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") } type PostsConnection { @@ -1061,21 +1287,15 @@ describe("Cypher", () => { } type Query { - blogs(limit: Int, offset: Int, options: BlogOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BlogSort!], where: BlogWhere): [Blog!]! + blogs(limit: Int, offset: Int, sort: [BlogSort!], where: BlogWhere): [Blog!]! blogsAggregate(where: BlogWhere): BlogAggregateSelection! blogsConnection(after: String, first: Int, sort: [BlogSort!], where: BlogWhere): BlogsConnection! - contents(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ContentWhere): [Content!]! - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + contents(limit: Int, offset: Int, where: ContentWhere): [Content!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -1089,6 +1309,20 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateBlogsMutationResponse { blogs: [Blog!]! info: UpdateInfo! @@ -1188,13 +1422,15 @@ describe("Cypher", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1205,8 +1441,8 @@ describe("Cypher", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { @@ -1214,17 +1450,17 @@ describe("Cypher", () => { NOT: ActorWhere OR: [ActorWhere!] movie: MovieWhere - movies: MovieWhere + movies: MovieRelationshipFilters movies_ALL: MovieWhere movies_NONE: MovieWhere movies_SINGLE: MovieWhere movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1280,9 +1516,15 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } input MovieUpdateInput { @@ -1297,7 +1539,7 @@ describe("Cypher", () => { NOT: MovieWhere OR: [MovieWhere!] actor: ActorWhere - actors: ActorWhere + actors: ActorRelationshipFilters actors_ALL: ActorWhere actors_NONE: ActorWhere actors_SINGLE: ActorWhere @@ -1345,17 +1587,11 @@ describe("Cypher", () => { Movie } - input ProductionOptions { - limit: Int - offset: Int - } - input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -1365,13 +1601,13 @@ describe("Cypher", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, where: ProductionWhere): ProductionsConnection! } @@ -1389,6 +1625,20 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1411,7 +1661,7 @@ describe("Cypher", () => { `); }); - test("Filters should be generated on 1:1 and *:* Relationship/Object custom cypher fields", async () => { + test("Filters should be generated only on 1:1 Relationship/Object custom cypher fields", async () => { const typeDefs = /* GraphQL */ ` type Movie @node { actors: [Actor] @@ -1483,13 +1733,15 @@ describe("Cypher", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1500,8 +1752,8 @@ describe("Cypher", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { @@ -1509,17 +1761,17 @@ describe("Cypher", () => { NOT: ActorWhere OR: [ActorWhere!] movie: MovieWhere - movies: MovieWhere + movies: MovieRelationshipFilters movies_ALL: MovieWhere movies_NONE: MovieWhere movies_SINGLE: MovieWhere movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1575,9 +1827,15 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } input MovieUpdateInput { @@ -1592,7 +1850,7 @@ describe("Cypher", () => { NOT: MovieWhere OR: [MovieWhere!] actor: ActorWhere - actors: ActorWhere + actors: ActorRelationshipFilters actors_ALL: ActorWhere actors_NONE: ActorWhere actors_SINGLE: ActorWhere @@ -1623,10 +1881,10 @@ describe("Cypher", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, where: MovieWhere): MoviesConnection! } @@ -1644,6 +1902,20 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1721,15 +1993,6 @@ describe("Cypher", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1739,27 +2002,27 @@ describe("Cypher", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - totalScreenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - totalScreenTime_EQ: Int - totalScreenTime_GT: Int - totalScreenTime_GTE: Int - totalScreenTime_IN: [Int!] - totalScreenTime_LT: Int - totalScreenTime_LTE: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + totalScreenTime: IntScalarFilters + totalScreenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { eq: ... }\\") + totalScreenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { gt: ... }\\") + totalScreenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { gte: ... }\\") + totalScreenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { in: ... }\\") + totalScreenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { lt: ... }\\") + totalScreenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter totalScreenTime: { lte: ... }\\") } type ActorsConnection { @@ -1794,9 +2057,28 @@ describe("Cypher", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { @@ -1806,7 +2088,6 @@ describe("Cypher", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -1818,15 +2099,6 @@ describe("Cypher", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1835,20 +2107,20 @@ describe("Cypher", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1875,10 +2147,10 @@ describe("Cypher", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1896,6 +2168,20 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2000,15 +2286,6 @@ describe("Cypher", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2021,17 +2298,17 @@ describe("Cypher", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -2045,18 +2322,18 @@ describe("Cypher", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - custom_title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_title_CONTAINS: String - custom_title_ENDS_WITH: String - custom_title_EQ: String - custom_title_IN: [String] - custom_title_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + custom_title: StringScalarFilters + custom_title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter custom_title: { contains: ... }\\") + custom_title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_title: { endsWith: ... }\\") + custom_title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter custom_title: { eq: ... }\\") + custom_title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter custom_title: { in: ... }\\") + custom_title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_title: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2080,7 +2357,7 @@ describe("Cypher", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -2098,6 +2375,20 @@ describe("Cypher", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! diff --git a/packages/graphql/tests/schema/directives/default.test.ts b/packages/graphql/tests/schema/directives/default.test.ts index 99e01dad0d..c0ae06f4f6 100644 --- a/packages/graphql/tests/schema/directives/default.test.ts +++ b/packages/graphql/tests/schema/directives/default.test.ts @@ -57,6 +57,16 @@ describe("@default directive", () => { mutation: Mutation } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -78,6 +88,21 @@ describe("@default directive", () => { min: DateTime } + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -93,9 +118,37 @@ describe("@default directive", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -105,12 +158,40 @@ describe("@default directive", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + enum Location { EVERYWHERE HERE THERE } + \\"\\"\\"Location filters\\"\\"\\" + input LocationEnumScalarFilters { + eq: Location + in: [Location!] + } + + \\"\\"\\"Location mutations\\"\\"\\" + input LocationEnumScalarMutations { + set: Location + } + type Mutation { createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! deleteUsers(where: UserWhere): DeleteInfo! @@ -126,10 +207,10 @@ describe("@default directive", () => { } type Query { - userInterfaces(limit: Int, offset: Int, options: UserInterfaceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! + userInterfaces(limit: Int, offset: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! userInterfacesAggregate(where: UserInterfaceWhere): UserInterfaceAggregateSelection! userInterfacesConnection(after: String, first: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): UserInterfacesConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -147,6 +228,20 @@ describe("@default directive", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -177,7 +272,6 @@ describe("@default directive", () => { type UserAggregateSelection { count: Int! fromInterface: StringAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! numberOfFriends: IntAggregateSelection! rating: FloatAggregateSelection! @@ -222,15 +316,6 @@ describe("@default directive", () => { User } - input UserInterfaceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserInterfaceSort objects to sort UserInterfaces by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserInterfaceSort!] - } - \\"\\"\\" Fields to sort UserInterfaces by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserInterfaceSort object. \\"\\"\\" @@ -243,20 +328,19 @@ describe("@default directive", () => { AND: [UserInterfaceWhere!] NOT: UserInterfaceWhere OR: [UserInterfaceWhere!] - fromInterface: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - fromInterface_CONTAINS: String - fromInterface_ENDS_WITH: String - fromInterface_EQ: String - fromInterface_IN: [String!] - fromInterface_STARTS_WITH: String - toBeOverridden: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - toBeOverridden_CONTAINS: String - toBeOverridden_ENDS_WITH: String - toBeOverridden_EQ: String - toBeOverridden_IN: [String!] - toBeOverridden_STARTS_WITH: String + fromInterface: StringScalarFilters + fromInterface_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { contains: ... }\\") + fromInterface_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { endsWith: ... }\\") + fromInterface_EQ: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { eq: ... }\\") + fromInterface_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { in: ... }\\") + fromInterface_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { startsWith: ... }\\") + toBeOverridden: StringScalarFilters + toBeOverridden_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { contains: ... }\\") + toBeOverridden_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { endsWith: ... }\\") + toBeOverridden_EQ: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { eq: ... }\\") + toBeOverridden_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { in: ... }\\") + toBeOverridden_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { startsWith: ... }\\") typename: [UserInterfaceImplementation!] - typename_IN: [UserInterfaceImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type UserInterfacesConnection { @@ -265,15 +349,6 @@ describe("@default directive", () => { totalCount: Int! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - \\"\\"\\" Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. \\"\\"\\" @@ -290,86 +365,86 @@ describe("@default directive", () => { } input UserUpdateInput { - fromInterface: String @deprecated(reason: \\"Please use the explicit _SET field\\") - fromInterface_SET: String - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - location: Location @deprecated(reason: \\"Please use the explicit _SET field\\") - location_SET: Location - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - numberOfFriends: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - numberOfFriends_DECREMENT: Int - numberOfFriends_INCREMENT: Int - numberOfFriends_SET: Int - rating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - rating_ADD: Float - rating_DIVIDE: Float - rating_MULTIPLY: Float - rating_SET: Float - rating_SUBTRACT: Float - toBeOverridden: String @deprecated(reason: \\"Please use the explicit _SET field\\") - toBeOverridden_SET: String - verified: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - verifiedDate: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - verifiedDate_SET: DateTime - verified_SET: Boolean + fromInterface: StringScalarMutations + fromInterface_SET: String @deprecated(reason: \\"Please use the generic mutation 'fromInterface: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + location: LocationEnumScalarMutations + location_SET: Location @deprecated(reason: \\"Please use the generic mutation 'location: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + numberOfFriends: IntScalarMutations + numberOfFriends_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'numberOfFriends: { decrement: ... } }' instead.\\") + numberOfFriends_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'numberOfFriends: { increment: ... } }' instead.\\") + numberOfFriends_SET: Int @deprecated(reason: \\"Please use the generic mutation 'numberOfFriends: { set: ... } }' instead.\\") + rating: FloatScalarMutations + rating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { add: ... } }' instead.\\") + rating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { divide: ... } }' instead.\\") + rating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { multiply: ... } }' instead.\\") + rating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'rating: { set: ... } }' instead.\\") + rating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'rating: { subtract: ... } }' instead.\\") + toBeOverridden: StringScalarMutations + toBeOverridden_SET: String @deprecated(reason: \\"Please use the generic mutation 'toBeOverridden: { set: ... } }' instead.\\") + verified: BooleanScalarMutations + verifiedDate: DateTimeScalarMutations + verifiedDate_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'verifiedDate: { set: ... } }' instead.\\") + verified_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'verified: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - fromInterface: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - fromInterface_CONTAINS: String - fromInterface_ENDS_WITH: String - fromInterface_EQ: String - fromInterface_IN: [String!] - fromInterface_STARTS_WITH: String - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - location: Location @deprecated(reason: \\"Please use the explicit _EQ version\\") - location_EQ: Location - location_IN: [Location!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - numberOfFriends: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - numberOfFriends_EQ: Int - numberOfFriends_GT: Int - numberOfFriends_GTE: Int - numberOfFriends_IN: [Int!] - numberOfFriends_LT: Int - numberOfFriends_LTE: Int - rating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - rating_EQ: Float - rating_GT: Float - rating_GTE: Float - rating_IN: [Float!] - rating_LT: Float - rating_LTE: Float - toBeOverridden: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - toBeOverridden_CONTAINS: String - toBeOverridden_ENDS_WITH: String - toBeOverridden_EQ: String - toBeOverridden_IN: [String!] - toBeOverridden_STARTS_WITH: String - verified: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - verifiedDate: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - verifiedDate_EQ: DateTime - verifiedDate_GT: DateTime - verifiedDate_GTE: DateTime - verifiedDate_IN: [DateTime!] - verifiedDate_LT: DateTime - verifiedDate_LTE: DateTime - verified_EQ: Boolean + fromInterface: StringScalarFilters + fromInterface_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { contains: ... }\\") + fromInterface_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { endsWith: ... }\\") + fromInterface_EQ: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { eq: ... }\\") + fromInterface_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { in: ... }\\") + fromInterface_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter fromInterface: { startsWith: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + location: LocationEnumScalarFilters + location_EQ: Location @deprecated(reason: \\"Please use the relevant generic filter location: { eq: ... }\\") + location_IN: [Location!] @deprecated(reason: \\"Please use the relevant generic filter location: { in: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + numberOfFriends: IntScalarFilters + numberOfFriends_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { eq: ... }\\") + numberOfFriends_GT: Int @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { gt: ... }\\") + numberOfFriends_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { gte: ... }\\") + numberOfFriends_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { in: ... }\\") + numberOfFriends_LT: Int @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { lt: ... }\\") + numberOfFriends_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter numberOfFriends: { lte: ... }\\") + rating: FloatScalarFilters + rating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { eq: ... }\\") + rating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { gt: ... }\\") + rating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { gte: ... }\\") + rating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter rating: { in: ... }\\") + rating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { lt: ... }\\") + rating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter rating: { lte: ... }\\") + toBeOverridden: StringScalarFilters + toBeOverridden_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { contains: ... }\\") + toBeOverridden_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { endsWith: ... }\\") + toBeOverridden_EQ: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { eq: ... }\\") + toBeOverridden_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { in: ... }\\") + toBeOverridden_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter toBeOverridden: { startsWith: ... }\\") + verified: BooleanScalarFilters + verifiedDate: DateTimeScalarFilters + verifiedDate_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { eq: ... }\\") + verifiedDate_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { gt: ... }\\") + verifiedDate_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { gte: ... }\\") + verifiedDate_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { in: ... }\\") + verifiedDate_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { lt: ... }\\") + verifiedDate_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter verifiedDate: { lte: ... }\\") + verified_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter verified: { eq: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/directives/filterable.test.ts b/packages/graphql/tests/schema/directives/filterable.test.ts index 66508076e1..f22781fe67 100644 --- a/packages/graphql/tests/schema/directives/filterable.test.ts +++ b/packages/graphql/tests/schema/directives/filterable.test.ts @@ -52,13 +52,13 @@ describe("@filterable directive", () => { const movieWhereFields = movieWhereType.getFields(); - const title = movieWhereFields["title"]; + const title_EQ = movieWhereFields["title_EQ"]; const title_IN = movieWhereFields["title_IN"]; const title_CONTAINS = movieWhereFields["title_CONTAINS"]; const title_STARTS_WITH = movieWhereFields["title_STARTS_WITH"]; const title_ENDS_WITH = movieWhereFields["title_ENDS_WITH"]; - const titleFilters = [title, title_IN, title_CONTAINS, title_STARTS_WITH, title_ENDS_WITH]; + const titleFilters = [title_EQ, title_IN, title_CONTAINS, title_STARTS_WITH, title_ENDS_WITH]; for (const scalarFilter of titleFilters) { expect(scalarFilter).toBeDefined(); @@ -70,14 +70,14 @@ describe("@filterable directive", () => { const movieSubscriptionWhereFields = movieSubscriptionWhereType.getFields(); - const subscriptionTitle = movieSubscriptionWhereFields["title"]; + const subscriptionTitle_EQ = movieSubscriptionWhereFields["title_EQ"]; const subscriptionTitle_IN = movieSubscriptionWhereFields["title_IN"]; const subscriptionTitle_CONTAINS = movieSubscriptionWhereFields["title_CONTAINS"]; const subscriptionTitle_STARTS_WITH = movieSubscriptionWhereFields["title_STARTS_WITH"]; const subscriptionTitle_ENDS_WITH = movieSubscriptionWhereFields["title_ENDS_WITH"]; const subscriptionTitleFilters = [ - subscriptionTitle, + subscriptionTitle_EQ, subscriptionTitle_IN, subscriptionTitle_CONTAINS, subscriptionTitle_STARTS_WITH, @@ -120,13 +120,13 @@ describe("@filterable directive", () => { expect(movieWhereType).toBeDefined(); const movieWhereFields = movieWhereType.getFields(); - const title = movieWhereFields["title"]; + const title_EQ = movieWhereFields["title_EQ"]; const title_IN = movieWhereFields["title_IN"]; const title_CONTAINS = movieWhereFields["title_CONTAINS"]; const title_STARTS_WITH = movieWhereFields["title_STARTS_WITH"]; const title_ENDS_WITH = movieWhereFields["title_ENDS_WITH"]; - const titleFilters = [title, title_IN, title_CONTAINS, title_STARTS_WITH, title_ENDS_WITH]; + const titleFilters = [title_EQ, title_IN, title_CONTAINS, title_STARTS_WITH, title_ENDS_WITH]; for (const scalarFilter of titleFilters) { expect(scalarFilter).toBeDefined(); @@ -138,14 +138,14 @@ describe("@filterable directive", () => { const movieSubscriptionWhereFields = movieSubscriptionWhereType.getFields(); - const subscriptionTitle = movieSubscriptionWhereFields["title"]; + const subscriptionTitle_EQ = movieSubscriptionWhereFields["title_EQ"]; const subscriptionTitle_IN = movieSubscriptionWhereFields["title_IN"]; const subscriptionTitle_CONTAINS = movieSubscriptionWhereFields["title_CONTAINS"]; const subscriptionTitle_STARTS_WITH = movieSubscriptionWhereFields["title_STARTS_WITH"]; const subscriptionTitle_ENDS_WITH = movieSubscriptionWhereFields["title_ENDS_WITH"]; const subscriptionTitleFilters = [ - subscriptionTitle, + subscriptionTitle_EQ, subscriptionTitle_IN, subscriptionTitle_CONTAINS, subscriptionTitle_STARTS_WITH, @@ -894,9 +894,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -964,7 +964,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -974,10 +974,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -987,6 +983,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -1035,13 +1050,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1056,26 +1073,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -1089,43 +1106,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -1168,10 +1187,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -1189,7 +1228,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1200,10 +1239,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1213,6 +1248,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -1247,36 +1301,38 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1344,13 +1400,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1364,18 +1422,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -1389,37 +1447,39 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1446,10 +1506,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1467,6 +1527,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -1527,9 +1608,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -1597,7 +1678,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1608,10 +1689,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1621,6 +1698,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -1655,21 +1751,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -1690,13 +1787,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1711,26 +1810,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -1744,43 +1843,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -1823,10 +1924,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -1844,7 +1965,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1855,10 +1976,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1868,6 +1985,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -1902,36 +2038,38 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1999,13 +2137,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -2019,18 +2159,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -2044,37 +2184,39 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2101,10 +2243,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -2122,6 +2264,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -2182,9 +2345,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -2252,7 +2415,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2263,10 +2426,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -2276,6 +2435,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -2310,21 +2488,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -2345,13 +2524,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -2366,26 +2547,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -2399,43 +2580,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -2478,10 +2661,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -2499,7 +2702,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2510,10 +2713,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2523,6 +2722,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -2557,36 +2775,38 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2654,13 +2874,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -2672,8 +2894,8 @@ describe("@filterable directive", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -2687,31 +2909,33 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") } type MoviesConnection { @@ -2738,10 +2962,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -2759,6 +2983,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -2822,9 +3067,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -2892,7 +3137,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2903,10 +3148,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -2916,6 +3157,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -2950,21 +3210,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -2985,13 +3246,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -3006,26 +3269,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -3039,43 +3302,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -3118,10 +3383,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -3137,10 +3422,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3150,6 +3431,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -3245,13 +3545,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -3265,18 +3567,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -3290,36 +3592,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -3346,10 +3650,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -3367,6 +3671,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -3429,9 +3754,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -3499,7 +3824,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3510,10 +3835,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -3523,6 +3844,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -3557,21 +3897,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -3592,13 +3933,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -3613,26 +3956,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -3646,43 +3989,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -3725,10 +4070,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -3746,7 +4111,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3757,10 +4122,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3770,6 +4131,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -3804,36 +4184,38 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -3901,13 +4283,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -3921,18 +4305,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -3946,37 +4330,39 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -4003,10 +4389,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -4024,6 +4410,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -4086,9 +4493,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -4156,7 +4563,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4167,10 +4574,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -4180,6 +4583,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -4214,21 +4636,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -4249,15 +4672,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -4270,26 +4684,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -4303,43 +4717,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -4382,11 +4798,31 @@ describe("@filterable directive", () => { UPDATE } - type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - title: String + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + type Movie { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + title: String } type MovieActorActorsAggregationSelection { @@ -4403,7 +4839,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4414,10 +4850,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -4461,36 +4893,38 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -4558,13 +4992,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -4578,18 +5014,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -4604,12 +5040,12 @@ describe("@filterable directive", () => { NOT: MovieWhere OR: [MovieWhere!] actorsAggregate: MovieActorsAggregateInput - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -4636,10 +5072,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -4657,6 +5093,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -4719,9 +5176,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -4789,7 +5246,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4800,10 +5257,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -4813,6 +5266,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -4847,21 +5319,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -4882,13 +5355,15 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -4903,26 +5378,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -4936,43 +5411,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -5015,10 +5492,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -5034,10 +5531,6 @@ describe("@filterable directive", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -5047,6 +5540,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -5142,13 +5654,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -5162,18 +5676,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -5187,36 +5701,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -5243,10 +5759,10 @@ describe("@filterable directive", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -5264,6 +5780,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -5331,9 +5868,9 @@ describe("@filterable directive", () => { } type Actor implements Person { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -5389,7 +5926,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5400,10 +5937,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -5413,6 +5946,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -5447,21 +5999,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -5482,15 +6035,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -5503,26 +6047,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -5536,43 +6080,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -5615,10 +6161,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -5632,6 +6198,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -5725,15 +6310,6 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -5743,6 +6319,17 @@ describe("@filterable directive", () => { username: StringAggregateSelection! } + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -5754,18 +6341,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -5779,36 +6366,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -5866,13 +6455,15 @@ describe("@filterable directive", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -5883,8 +6474,8 @@ describe("@filterable directive", () => { } input PersonUpdateInput { - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input PersonWhere { @@ -5892,23 +6483,22 @@ describe("@filterable directive", () => { NOT: PersonWhere OR: [PersonWhere!] typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -5926,6 +6516,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -5993,9 +6604,9 @@ describe("@filterable directive", () => { } type Actor implements Person { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -6051,7 +6662,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6062,10 +6673,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -6075,6 +6682,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -6109,21 +6735,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -6144,15 +6771,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -6165,26 +6783,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -6198,43 +6816,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -6277,10 +6897,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -6288,7 +6928,7 @@ describe("@filterable directive", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6307,6 +6947,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -6339,21 +6998,22 @@ describe("@filterable directive", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -6421,15 +7081,6 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -6439,6 +7090,17 @@ describe("@filterable directive", () => { username: StringAggregateSelection! } + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -6450,18 +7112,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -6475,37 +7137,39 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -6563,13 +7227,15 @@ describe("@filterable directive", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -6580,8 +7246,8 @@ describe("@filterable directive", () => { } input PersonUpdateInput { - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input PersonWhere { @@ -6589,23 +7255,22 @@ describe("@filterable directive", () => { NOT: PersonWhere OR: [PersonWhere!] typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -6623,6 +7288,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -6690,9 +7376,9 @@ describe("@filterable directive", () => { } type Actor implements Person { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -6748,7 +7434,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6759,10 +7445,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -6772,6 +7454,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -6806,21 +7507,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -6841,15 +7543,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -6862,26 +7555,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -6895,43 +7588,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -6974,10 +7669,30 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -6991,6 +7706,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -7084,15 +7818,6 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -7102,6 +7827,17 @@ describe("@filterable directive", () => { username: StringAggregateSelection! } + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -7113,18 +7849,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -7138,36 +7874,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -7225,13 +7963,15 @@ describe("@filterable directive", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -7242,8 +7982,8 @@ describe("@filterable directive", () => { } input PersonUpdateInput { - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input PersonWhere { @@ -7251,23 +7991,22 @@ describe("@filterable directive", () => { NOT: PersonWhere OR: [PersonWhere!] typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -7285,6 +8024,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -7356,9 +8116,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -7426,7 +8186,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7437,10 +8197,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -7450,6 +8206,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -7484,21 +8259,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -7519,15 +8295,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -7540,26 +8307,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -7573,43 +8340,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -7619,9 +8388,9 @@ describe("@filterable directive", () => { } type Appearance { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): AppearanceMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): AppearanceMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! password: String! username: String! } @@ -7689,7 +8458,7 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesAggregateInput!] NOT: AppearanceMoviesAggregateInput OR: [AppearanceMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7700,10 +8469,6 @@ describe("@filterable directive", () => { input AppearanceMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -7713,6 +8478,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input AppearanceMoviesConnectionFilters { + \\"\\"\\" + Return Appearances where all of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + all: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where none of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + none: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where one of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + single: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where some of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + some: AppearanceMoviesConnectionWhere + } + input AppearanceMoviesConnectionSort { node: MovieSort } @@ -7747,21 +8531,22 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesNodeAggregationWhereInput!] NOT: AppearanceMoviesNodeAggregationWhereInput OR: [AppearanceMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type AppearanceMoviesRelationship { @@ -7782,15 +8567,6 @@ describe("@filterable directive", () => { where: AppearanceMoviesConnectionWhere } - input AppearanceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AppearanceSort objects to sort Appearances by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AppearanceSort!] - } - \\"\\"\\" Fields to sort Appearances by. The order in which sorts are applied is not guaranteed when specifying many fields in one AppearanceSort object. \\"\\"\\" @@ -7803,26 +8579,26 @@ describe("@filterable directive", () => { AND: [AppearanceSubscriptionWhere!] NOT: AppearanceSubscriptionWhere OR: [AppearanceSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input AppearanceUpdateInput { movies: [AppearanceMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type AppearanceUpdatedEvent { @@ -7836,43 +8612,45 @@ describe("@filterable directive", () => { AND: [AppearanceWhere!] NOT: AppearanceWhere OR: [AppearanceWhere!] + movies: MovieRelationshipFilters moviesAggregate: AppearanceMoviesAggregateInput + moviesConnection: AppearanceMoviesConnectionFilters \\"\\"\\" Return Appearances where all of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: AppearanceMoviesConnectionWhere + moviesConnection_ALL: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where none of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: AppearanceMoviesConnectionWhere + moviesConnection_NONE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where one of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: AppearanceMoviesConnectionWhere + moviesConnection_SINGLE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where some of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: AppearanceMoviesConnectionWhere + moviesConnection_SOME: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Appearances where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Appearances where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Appearances where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Appearances where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type AppearancesConnection { @@ -7920,9 +8698,29 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -8025,6 +8823,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Actor: MovieActorsActorConnectionWhere Appearance: MovieActorsAppearanceConnectionWhere @@ -8102,13 +8919,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -8122,18 +8941,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: MovieActorsUpdateInput - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -8147,36 +8966,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -8207,28 +9028,33 @@ describe("@filterable directive", () => { union Person = Actor | Appearance + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + input PersonWhere { Actor: ActorWhere Appearance: AppearanceWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - appearances(limit: Int, offset: Int, options: AppearanceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! + appearances(limit: Int, offset: Int, sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! appearancesAggregate(where: AppearanceWhere): AppearanceAggregateSelection! appearancesConnection(after: String, first: Int, sort: [AppearanceSort!], where: AppearanceWhere): AppearancesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -8244,6 +9070,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -8323,9 +9170,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -8393,7 +9240,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8404,10 +9251,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -8417,6 +9260,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -8451,21 +9313,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -8486,15 +9349,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -8507,26 +9361,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -8540,43 +9394,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -8586,9 +9442,9 @@ describe("@filterable directive", () => { } type Appearance { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): AppearanceMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): AppearanceMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! password: String! username: String! } @@ -8656,7 +9512,7 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesAggregateInput!] NOT: AppearanceMoviesAggregateInput OR: [AppearanceMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8667,10 +9523,6 @@ describe("@filterable directive", () => { input AppearanceMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -8680,6 +9532,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input AppearanceMoviesConnectionFilters { + \\"\\"\\" + Return Appearances where all of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + all: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where none of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + none: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where one of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + single: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where some of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + some: AppearanceMoviesConnectionWhere + } + input AppearanceMoviesConnectionSort { node: MovieSort } @@ -8714,21 +9585,22 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesNodeAggregationWhereInput!] NOT: AppearanceMoviesNodeAggregationWhereInput OR: [AppearanceMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type AppearanceMoviesRelationship { @@ -8749,15 +9621,6 @@ describe("@filterable directive", () => { where: AppearanceMoviesConnectionWhere } - input AppearanceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AppearanceSort objects to sort Appearances by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AppearanceSort!] - } - \\"\\"\\" Fields to sort Appearances by. The order in which sorts are applied is not guaranteed when specifying many fields in one AppearanceSort object. \\"\\"\\" @@ -8770,26 +9633,26 @@ describe("@filterable directive", () => { AND: [AppearanceSubscriptionWhere!] NOT: AppearanceSubscriptionWhere OR: [AppearanceSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input AppearanceUpdateInput { movies: [AppearanceMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type AppearanceUpdatedEvent { @@ -8803,43 +9666,45 @@ describe("@filterable directive", () => { AND: [AppearanceWhere!] NOT: AppearanceWhere OR: [AppearanceWhere!] + movies: MovieRelationshipFilters moviesAggregate: AppearanceMoviesAggregateInput + moviesConnection: AppearanceMoviesConnectionFilters \\"\\"\\" Return Appearances where all of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: AppearanceMoviesConnectionWhere + moviesConnection_ALL: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where none of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: AppearanceMoviesConnectionWhere + moviesConnection_NONE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where one of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: AppearanceMoviesConnectionWhere + moviesConnection_SINGLE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where some of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: AppearanceMoviesConnectionWhere + moviesConnection_SOME: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Appearances where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Appearances where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Appearances where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Appearances where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type AppearancesConnection { @@ -8887,9 +9752,29 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -8992,6 +9877,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Actor: MovieActorsActorConnectionWhere Appearance: MovieActorsAppearanceConnectionWhere @@ -9069,13 +9973,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -9089,18 +9995,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: MovieActorsUpdateInput - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -9114,36 +10020,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -9174,28 +10082,33 @@ describe("@filterable directive", () => { union Person = Actor | Appearance + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + input PersonWhere { Actor: ActorWhere Appearance: AppearanceWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - appearances(limit: Int, offset: Int, options: AppearanceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! + appearances(limit: Int, offset: Int, sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! appearancesAggregate(where: AppearanceWhere): AppearanceAggregateSelection! appearancesConnection(after: String, first: Int, sort: [AppearanceSort!], where: AppearanceWhere): AppearancesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -9211,6 +10124,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -9290,9 +10224,9 @@ describe("@filterable directive", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! password: String! username: String! } @@ -9360,7 +10294,7 @@ describe("@filterable directive", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9371,10 +10305,6 @@ describe("@filterable directive", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -9384,6 +10314,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -9418,21 +10367,22 @@ describe("@filterable directive", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -9453,15 +10403,6 @@ describe("@filterable directive", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -9474,26 +10415,26 @@ describe("@filterable directive", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -9507,43 +10448,45 @@ describe("@filterable directive", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -9553,9 +10496,9 @@ describe("@filterable directive", () => { } type Appearance { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): AppearanceMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): AppearanceMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [AppearanceMoviesConnectionSort!], where: AppearanceMoviesConnectionWhere): AppearanceMoviesConnection! password: String! username: String! } @@ -9623,7 +10566,7 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesAggregateInput!] NOT: AppearanceMoviesAggregateInput OR: [AppearanceMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9634,10 +10577,6 @@ describe("@filterable directive", () => { input AppearanceMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -9647,6 +10586,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input AppearanceMoviesConnectionFilters { + \\"\\"\\" + Return Appearances where all of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + all: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where none of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + none: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where one of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + single: AppearanceMoviesConnectionWhere + \\"\\"\\" + Return Appearances where some of the related AppearanceMoviesConnections match this filter + \\"\\"\\" + some: AppearanceMoviesConnectionWhere + } + input AppearanceMoviesConnectionSort { node: MovieSort } @@ -9681,21 +10639,22 @@ describe("@filterable directive", () => { AND: [AppearanceMoviesNodeAggregationWhereInput!] NOT: AppearanceMoviesNodeAggregationWhereInput OR: [AppearanceMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type AppearanceMoviesRelationship { @@ -9716,15 +10675,6 @@ describe("@filterable directive", () => { where: AppearanceMoviesConnectionWhere } - input AppearanceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AppearanceSort objects to sort Appearances by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AppearanceSort!] - } - \\"\\"\\" Fields to sort Appearances by. The order in which sorts are applied is not guaranteed when specifying many fields in one AppearanceSort object. \\"\\"\\" @@ -9737,26 +10687,26 @@ describe("@filterable directive", () => { AND: [AppearanceSubscriptionWhere!] NOT: AppearanceSubscriptionWhere OR: [AppearanceSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input AppearanceUpdateInput { movies: [AppearanceMoviesUpdateFieldInput!] - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type AppearanceUpdatedEvent { @@ -9770,43 +10720,45 @@ describe("@filterable directive", () => { AND: [AppearanceWhere!] NOT: AppearanceWhere OR: [AppearanceWhere!] + movies: MovieRelationshipFilters moviesAggregate: AppearanceMoviesAggregateInput + moviesConnection: AppearanceMoviesConnectionFilters \\"\\"\\" Return Appearances where all of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: AppearanceMoviesConnectionWhere + moviesConnection_ALL: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where none of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: AppearanceMoviesConnectionWhere + moviesConnection_NONE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where one of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: AppearanceMoviesConnectionWhere + moviesConnection_SINGLE: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Appearances where some of the related AppearanceMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: AppearanceMoviesConnectionWhere + moviesConnection_SOME: AppearanceMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Appearances where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Appearances where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Appearances where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Appearances where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type AppearancesConnection { @@ -9854,9 +10806,29 @@ describe("@filterable directive", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -9959,6 +10931,25 @@ describe("@filterable directive", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Actor: MovieActorsActorConnectionWhere Appearance: MovieActorsAppearanceConnectionWhere @@ -10036,13 +11027,15 @@ describe("@filterable directive", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -10056,18 +11049,18 @@ describe("@filterable directive", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { actors: MovieActorsUpdateInput - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -10081,36 +11074,38 @@ describe("@filterable directive", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -10141,28 +11136,33 @@ describe("@filterable directive", () => { union Person = Actor | Appearance + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + input PersonWhere { Actor: ActorWhere Appearance: AppearanceWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - appearances(limit: Int, offset: Int, options: AppearanceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! + appearances(limit: Int, offset: Int, sort: [AppearanceSort!], where: AppearanceWhere): [Appearance!]! appearancesAggregate(where: AppearanceWhere): AppearanceAggregateSelection! appearancesConnection(after: String, first: Int, sort: [AppearanceSort!], where: AppearanceWhere): AppearancesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -10178,6 +11178,27 @@ describe("@filterable directive", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! diff --git a/packages/graphql/tests/schema/directives/node.test.ts b/packages/graphql/tests/schema/directives/node.test.ts new file mode 100644 index 0000000000..b627a22409 --- /dev/null +++ b/packages/graphql/tests/schema/directives/node.test.ts @@ -0,0 +1,679 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../../src"; + +describe("@node", () => { + test("when @node is not applied it should not be considered for schema augmentation", async () => { + const typeDefs = /* GraphQL */ ` + type Actor @node { + name: String + } + ## Movie should not be considered for schema augmentation + type Movie { + id: ID + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + type Actor { + name: String + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorCreateInput { + name: String + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + name: SortDirection + } + + input ActorUpdateInput { + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + deleteActors(where: ActorWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Query { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + }" + `); + }); + + test("when @node is not applied should not be used in the augmented interface types", async () => { + const typeDefs = /* GraphQL */ ` + type Actor @node { + name: String + } + + type Movie implements Production { + name: String + } + + type Series implements Production @node { + name: String + } + + interface Production { + name: String + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + type Actor { + name: String + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorCreateInput { + name: String + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + name: SortDirection + } + + input ActorUpdateInput { + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateSeriesMutationResponse { + info: CreateInfo! + series: [Series!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! + deleteActors(where: ActorWhere): DeleteInfo! + deleteSeries(where: SeriesWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + interface Production { + name: String + } + + type ProductionAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + type ProductionEdge { + cursor: String! + node: Production! + } + + enum ProductionImplementation { + Series + } + + \\"\\"\\" + Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. + \\"\\"\\" + input ProductionSort { + name: SortDirection + } + + input ProductionWhere { + AND: [ProductionWhere!] + NOT: ProductionWhere + OR: [ProductionWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + typename: [ProductionImplementation!] + } + + type ProductionsConnection { + edges: [ProductionEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! + productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! + seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! + } + + type Series implements Production { + name: String + } + + type SeriesAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + type SeriesConnection { + edges: [SeriesEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input SeriesCreateInput { + name: String + } + + type SeriesEdge { + cursor: String! + node: Series! + } + + \\"\\"\\" + Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. + \\"\\"\\" + input SeriesSort { + name: SortDirection + } + + input SeriesUpdateInput { + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input SeriesWhere { + AND: [SeriesWhere!] + NOT: SeriesWhere + OR: [SeriesWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateSeriesMutationResponse { + info: UpdateInfo! + series: [Series!]! + }" + `); + }); + + test("when @node is not applied should not be used in the augmented union types", async () => { + const typeDefs = /* GraphQL */ ` + type Actor @node { + name: String + } + type Movie { + name: String + } + type Series @node { + name: String + } + + ## Production should not be considered for schema augmentation as it has no implemented nodes. + union Production = Movie | Series + `; + const neoSchema = new Neo4jGraphQL({ typeDefs }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + type Actor { + name: String + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorCreateInput { + name: String + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + name: SortDirection + } + + input ActorUpdateInput { + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateSeriesMutationResponse { + info: CreateInfo! + series: [Series!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + type Movie { + name: String + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! + deleteActors(where: ActorWhere): DeleteInfo! + deleteSeries(where: SeriesWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + union Production = Movie | Series + + input ProductionWhere { + Series: SeriesWhere + } + + type Query { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! + seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! + } + + type Series { + name: String + } + + type SeriesAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + type SeriesConnection { + edges: [SeriesEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input SeriesCreateInput { + name: String + } + + type SeriesEdge { + cursor: String! + node: Series! + } + + \\"\\"\\" + Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. + \\"\\"\\" + input SeriesSort { + name: SortDirection + } + + input SeriesUpdateInput { + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input SeriesWhere { + AND: [SeriesWhere!] + NOT: SeriesWhere + OR: [SeriesWhere!] + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateSeriesMutationResponse { + info: UpdateInfo! + series: [Series!]! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/directives/plural.test.ts b/packages/graphql/tests/schema/directives/plural.test.ts index fc73835c95..d8d75bbee5 100644 --- a/packages/graphql/tests/schema/directives/plural.test.ts +++ b/packages/graphql/tests/schema/directives/plural.test.ts @@ -18,6 +18,7 @@ */ import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { GraphQLError } from "graphql"; import { gql } from "graphql-tag"; import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; @@ -78,7 +79,7 @@ describe("Plural option", () => { } type Query { - techs(limit: Int, offset: Int, options: TechOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TechSort!], where: TechWhere): [Tech!]! + techs(limit: Int, offset: Int, sort: [TechSort!], where: TechWhere): [Tech!]! techsAggregate(where: TechWhere): TechAggregateSelection! techsConnection(after: String, first: Int, sort: [TechSort!], where: TechWhere): TechsConnection! } @@ -96,6 +97,20 @@ describe("Plural option", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Tech { name: String value: String @@ -117,15 +132,6 @@ describe("Plural option", () => { node: Tech! } - input TechOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TechSort objects to sort Techs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TechSort!] - } - \\"\\"\\" Fields to sort Techs by. The order in which sorts are applied is not guaranteed when specifying many fields in one TechSort object. \\"\\"\\" @@ -135,28 +141,28 @@ describe("Plural option", () => { } input TechUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + value: StringScalarMutations + value_SET: String @deprecated(reason: \\"Please use the generic mutation 'value: { set: ... } }' instead.\\") } input TechWhere { AND: [TechWhere!] NOT: TechWhere OR: [TechWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + value: StringScalarFilters + value_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter value: { contains: ... }\\") + value_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { endsWith: ... }\\") + value_EQ: String @deprecated(reason: \\"Please use the relevant generic filter value: { eq: ... }\\") + value_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter value: { in: ... }\\") + value_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { startsWith: ... }\\") } type TechsConnection { @@ -237,7 +243,7 @@ describe("Plural option", () => { } type Query { - techs(limit: Int, offset: Int, options: TechOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TechSort!], where: TechWhere): [Tech!]! + techs(limit: Int, offset: Int, sort: [TechSort!], where: TechWhere): [Tech!]! techsAggregate(where: TechWhere): TechAggregateSelection! techsConnection(after: String, first: Int, sort: [TechSort!], where: TechWhere): TechsConnection! } @@ -255,6 +261,20 @@ describe("Plural option", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Tech { name: String value: String @@ -276,15 +296,6 @@ describe("Plural option", () => { node: Tech! } - input TechOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TechSort objects to sort Techs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TechSort!] - } - \\"\\"\\" Fields to sort Techs by. The order in which sorts are applied is not guaranteed when specifying many fields in one TechSort object. \\"\\"\\" @@ -294,28 +305,28 @@ describe("Plural option", () => { } input TechUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + value: StringScalarMutations + value_SET: String @deprecated(reason: \\"Please use the generic mutation 'value: { set: ... } }' instead.\\") } input TechWhere { AND: [TechWhere!] NOT: TechWhere OR: [TechWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + value: StringScalarFilters + value_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter value: { contains: ... }\\") + value_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { endsWith: ... }\\") + value_EQ: String @deprecated(reason: \\"Please use the relevant generic filter value: { eq: ... }\\") + value_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter value: { in: ... }\\") + value_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { startsWith: ... }\\") } type TechsConnection { @@ -396,7 +407,7 @@ describe("Plural option", () => { } type Query { - technologies(limit: Int, offset: Int, options: TechOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TechSort!], where: TechWhere): [Tech!]! + technologies(limit: Int, offset: Int, sort: [TechSort!], where: TechWhere): [Tech!]! technologiesAggregate(where: TechWhere): TechAggregateSelection! technologiesConnection(after: String, first: Int, sort: [TechSort!], where: TechWhere): TechnologiesConnection! } @@ -414,6 +425,20 @@ describe("Plural option", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Tech { name: String value: String @@ -435,15 +460,6 @@ describe("Plural option", () => { node: Tech! } - input TechOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TechSort objects to sort Technologies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TechSort!] - } - \\"\\"\\" Fields to sort Technologies by. The order in which sorts are applied is not guaranteed when specifying many fields in one TechSort object. \\"\\"\\" @@ -453,28 +469,28 @@ describe("Plural option", () => { } input TechUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + value: StringScalarMutations + value_SET: String @deprecated(reason: \\"Please use the generic mutation 'value: { set: ... } }' instead.\\") } input TechWhere { AND: [TechWhere!] NOT: TechWhere OR: [TechWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + value: StringScalarFilters + value_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter value: { contains: ... }\\") + value_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { endsWith: ... }\\") + value_EQ: String @deprecated(reason: \\"Please use the relevant generic filter value: { eq: ... }\\") + value_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter value: { in: ... }\\") + value_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter value: { startsWith: ... }\\") } type TechnologiesConnection { @@ -500,153 +516,6 @@ describe("Plural option", () => { `); }); - test("Collision between Type and plural", async () => { - const typeDefs = gql` - type Tech @plural(value: "Techs") @node { - name: String - } - - type Techs @node { - value: String - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateTechsMutationResponse { - info: CreateInfo! - techs: [Techs!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createTechs(input: [TechsCreateInput!]!): CreateTechsMutationResponse! - deleteTechs(where: TechsWhere): DeleteInfo! - updateTechs(update: TechsUpdateInput, where: TechsWhere): UpdateTechsMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - techs(limit: Int, offset: Int, options: TechsOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TechsSort!], where: TechsWhere): [Techs!]! - techsAggregate(where: TechsWhere): TechsAggregateSelection! - techsConnection(after: String, first: Int, sort: [TechsSort!], where: TechsWhere): TechsConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Techs { - value: String - } - - type TechsAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - type TechsConnection { - edges: [TechsEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input TechsCreateInput { - value: String - } - - type TechsEdge { - cursor: String! - node: Techs! - } - - input TechsOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TechsSort objects to sort Techs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TechsSort!] - } - - \\"\\"\\" - Fields to sort Techs by. The order in which sorts are applied is not guaranteed when specifying many fields in one TechsSort object. - \\"\\"\\" - input TechsSort { - value: SortDirection - } - - input TechsUpdateInput { - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input TechsWhere { - AND: [TechsWhere!] - NOT: TechsWhere - OR: [TechsWhere!] - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateTechsMutationResponse { - info: UpdateInfo! - techs: [Techs!]! - }" - `); - }); - test("Same plural on multiple nodes", async () => { const typeDefs = gql` type Tech @plural(value: "Techs") @node { @@ -657,288 +526,13 @@ describe("Plural option", () => { value: String } `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateTechsMutationResponse { - info: CreateInfo! - techs: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createTechs(input: [UserCreateInput!]!): CreateTechsMutationResponse! - deleteTechs(where: UserWhere): DeleteInfo! - updateTechs(update: UserUpdateInput, where: UserWhere): UpdateTechsMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - techs(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - techsAggregate(where: UserWhere): UserAggregateSelection! - techsConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): TechsConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type TechsConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateTechsMutationResponse { - info: UpdateInfo! - techs: [User!]! - } - - type User { - value: String - } - - type UserAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - input UserCreateInput { - value: String - } - - type UserEdge { - cursor: String! - node: User! - } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Techs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Techs by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - value: SortDirection - } - - input UserUpdateInput { - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String - }" - `); - }); - - test("Collision with pluralize", async () => { - const typeDefs = gql` - type Tech @plural(value: "Users") @node { - name: String - } - - type User @node { - value: String - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - value: String - } - - type UserAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - input UserCreateInput { - value: String - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - value: SortDirection - } - - input UserUpdateInput { - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); + await expect(async () => { + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + await neoSchema.getSchema(); + }).rejects.toIncludeSameMembers([new GraphQLError(`Ambiguous plural "techs" in "User"`)]); }); test("Type collision with pluralize", async () => { @@ -951,140 +545,11 @@ describe("Plural option", () => { value: String } `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [Users!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createUsers(input: [UsersCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(where: UsersWhere): DeleteInfo! - updateUsers(update: UsersUpdateInput, where: UsersWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - users(limit: Int, offset: Int, options: UsersOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UsersSort!], where: UsersWhere): [Users!]! - usersAggregate(where: UsersWhere): UsersAggregateSelection! - usersConnection(after: String, first: Int, sort: [UsersSort!], where: UsersWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [Users!]! - } - - type Users { - value: String - } - - type UsersAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - type UsersConnection { - edges: [UsersEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UsersCreateInput { - value: String - } - - type UsersEdge { - cursor: String! - node: Users! - } - - input UsersOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UsersSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UsersSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UsersSort object. - \\"\\"\\" - input UsersSort { - value: SortDirection - } - - input UsersUpdateInput { - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input UsersWhere { - AND: [UsersWhere!] - NOT: UsersWhere - OR: [UsersWhere!] - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String] - value_STARTS_WITH: String - }" - `); + await expect(async () => { + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + await neoSchema.getSchema(); + }).rejects.toIncludeSameMembers([new GraphQLError(`Ambiguous plural "users" in "Users"`)]); }); }); diff --git a/packages/graphql/tests/schema/directives/populatedBy.test.ts b/packages/graphql/tests/schema/directives/populatedBy.test.ts index 734ca1c724..8aa87ae7ee 100644 --- a/packages/graphql/tests/schema/directives/populatedBy.test.ts +++ b/packages/graphql/tests/schema/directives/populatedBy.test.ts @@ -183,9 +183,18 @@ describe("@populatedBy tests", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -200,7 +209,6 @@ describe("@populatedBy tests", () => { callback2: StringAggregateSelection! callback3: StringAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -213,15 +221,6 @@ describe("@populatedBy tests", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -233,40 +232,40 @@ describe("@populatedBy tests", () => { } input MovieUpdateInput { - callback1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - callback1_SET: String - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + callback1: StringScalarMutations + callback1_SET: String @deprecated(reason: \\"Please use the generic mutation 'callback1: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - callback1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback1_CONTAINS: String - callback1_ENDS_WITH: String - callback1_EQ: String - callback1_IN: [String!] - callback1_STARTS_WITH: String - callback2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback2_CONTAINS: String - callback2_ENDS_WITH: String - callback2_EQ: String - callback2_IN: [String!] - callback2_STARTS_WITH: String - callback3: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback3_CONTAINS: String - callback3_ENDS_WITH: String - callback3_EQ: String - callback3_IN: [String!] - callback3_STARTS_WITH: String - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + callback1: StringScalarFilters + callback1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { contains: ... }\\") + callback1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { endsWith: ... }\\") + callback1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { eq: ... }\\") + callback1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback1: { in: ... }\\") + callback1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { startsWith: ... }\\") + callback2: StringScalarFilters + callback2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { contains: ... }\\") + callback2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { endsWith: ... }\\") + callback2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { eq: ... }\\") + callback2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback2: { in: ... }\\") + callback2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { startsWith: ... }\\") + callback3: StringScalarFilters + callback3_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { contains: ... }\\") + callback3_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { endsWith: ... }\\") + callback3_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { eq: ... }\\") + callback3_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback3: { in: ... }\\") + callback3_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { startsWith: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -290,7 +289,7 @@ describe("@populatedBy tests", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -308,6 +307,20 @@ describe("@populatedBy tests", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -381,9 +394,18 @@ describe("@populatedBy tests", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -393,6 +415,23 @@ describe("@populatedBy tests", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { callback1: Int! callback2: Int! @@ -405,7 +444,6 @@ describe("@populatedBy tests", () => { callback2: IntAggregateSelection! callback3: IntAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -418,15 +456,6 @@ describe("@populatedBy tests", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -438,45 +467,45 @@ describe("@populatedBy tests", () => { } input MovieUpdateInput { - callback1: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - callback1_DECREMENT: Int - callback1_INCREMENT: Int - callback1_SET: Int - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + callback1: IntScalarMutations + callback1_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'callback1: { decrement: ... } }' instead.\\") + callback1_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'callback1: { increment: ... } }' instead.\\") + callback1_SET: Int @deprecated(reason: \\"Please use the generic mutation 'callback1: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - callback1: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback1_EQ: Int - callback1_GT: Int - callback1_GTE: Int - callback1_IN: [Int!] - callback1_LT: Int - callback1_LTE: Int - callback2: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback2_EQ: Int - callback2_GT: Int - callback2_GTE: Int - callback2_IN: [Int!] - callback2_LT: Int - callback2_LTE: Int - callback3: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback3_EQ: Int - callback3_GT: Int - callback3_GTE: Int - callback3_IN: [Int!] - callback3_LT: Int - callback3_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + callback1: IntScalarFilters + callback1_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { eq: ... }\\") + callback1_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { gt: ... }\\") + callback1_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { gte: ... }\\") + callback1_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback1: { in: ... }\\") + callback1_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { lt: ... }\\") + callback1_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { lte: ... }\\") + callback2: IntScalarFilters + callback2_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { eq: ... }\\") + callback2_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { gt: ... }\\") + callback2_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { gte: ... }\\") + callback2_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback2: { in: ... }\\") + callback2_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { lt: ... }\\") + callback2_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { lte: ... }\\") + callback3: IntScalarFilters + callback3_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { eq: ... }\\") + callback3_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { gt: ... }\\") + callback3_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { gte: ... }\\") + callback3_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback3: { in: ... }\\") + callback3_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { lt: ... }\\") + callback3_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -500,7 +529,7 @@ describe("@populatedBy tests", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -735,13 +764,22 @@ describe("@populatedBy tests", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type Genre { id: ID! } type GenreAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input GenreConnectWhere { @@ -757,13 +795,15 @@ describe("@populatedBy tests", () => { node: Genre! } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] + input GenreRelationshipFilters { + \\"\\"\\"Filter type where all of the related Genres match this filter\\"\\"\\" + all: GenreWhere + \\"\\"\\"Filter type where none of the related Genres match this filter\\"\\"\\" + none: GenreWhere + \\"\\"\\"Filter type where one of the related Genres match this filter\\"\\"\\" + single: GenreWhere + \\"\\"\\"Filter type where some of the related Genres match this filter\\"\\"\\" + some: GenreWhere } \\"\\"\\" @@ -774,20 +814,20 @@ describe("@populatedBy tests", () => { } input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type GenresConnection { @@ -796,21 +836,39 @@ describe("@populatedBy tests", () => { totalCount: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - genres(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenresAggregationSelection - genresConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! + genresAggregate(where: GenreWhere): MovieGenreGenresAggregationSelection + genresConnection(after: String, first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! id: ID } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -830,40 +888,29 @@ describe("@populatedBy tests", () => { type MovieGenreGenresAggregationSelection { count: Int! edge: MovieGenreGenresEdgeAggregateSelection - node: MovieGenreGenresNodeAggregateSelection } type MovieGenreGenresEdgeAggregateSelection { callback1: StringAggregateSelection! callback2: StringAggregateSelection! callback3: StringAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type MovieGenreGenresNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieGenresAggregateInput { AND: [MovieGenresAggregateInput!] NOT: MovieGenresAggregateInput OR: [MovieGenresAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int edge: RelPropertiesAggregationWhereInput - node: MovieGenresNodeAggregationWhereInput } input MovieGenresConnectFieldInput { edge: RelPropertiesCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: GenreConnectWhere } @@ -873,6 +920,25 @@ describe("@populatedBy tests", () => { totalCount: Int! } + input MovieGenresConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieGenresConnections match this filter + \\"\\"\\" + all: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieGenresConnections match this filter + \\"\\"\\" + none: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieGenresConnections match this filter + \\"\\"\\" + single: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieGenresConnections match this filter + \\"\\"\\" + some: MovieGenresConnectionWhere + } + input MovieGenresConnectionSort { edge: RelPropertiesSort node: GenreSort @@ -904,22 +970,6 @@ describe("@populatedBy tests", () => { create: [MovieGenresCreateFieldInput!] } - input MovieGenresNodeAggregationWhereInput { - AND: [MovieGenresNodeAggregationWhereInput!] - NOT: MovieGenresNodeAggregationWhereInput - OR: [MovieGenresNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type MovieGenresRelationship { cursor: String! node: Genre! @@ -940,15 +990,6 @@ describe("@populatedBy tests", () => { where: MovieGenresConnectionWhere } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -958,45 +999,47 @@ describe("@populatedBy tests", () => { input MovieUpdateInput { genres: [MovieGenresUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + genres: GenreRelationshipFilters genresAggregate: MovieGenresAggregateInput + genresConnection: MovieGenresConnectionFilters \\"\\"\\" Return Movies where all of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_ALL: MovieGenresConnectionWhere + genresConnection_ALL: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_NONE: MovieGenresConnectionWhere + genresConnection_NONE: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_SINGLE: MovieGenresConnectionWhere + genresConnection_SINGLE: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_SOME: MovieGenresConnectionWhere + genresConnection_SOME: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Genres match this filter\\"\\"\\" - genres_ALL: GenreWhere + genres_ALL: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Genres match this filter\\"\\"\\" - genres_NONE: GenreWhere + genres_NONE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Genres match this filter\\"\\"\\" - genres_SINGLE: GenreWhere + genres_SINGLE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Genres match this filter\\"\\"\\" - genres_SOME: GenreWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + genres_SOME: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1023,10 +1066,10 @@ describe("@populatedBy tests", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1046,61 +1089,54 @@ describe("@populatedBy tests", () => { AND: [RelPropertiesAggregationWhereInput!] NOT: RelPropertiesAggregationWhereInput OR: [RelPropertiesAggregationWhereInput!] - callback1_AVERAGE_LENGTH_EQUAL: Float - callback1_AVERAGE_LENGTH_GT: Float - callback1_AVERAGE_LENGTH_GTE: Float - callback1_AVERAGE_LENGTH_LT: Float - callback1_AVERAGE_LENGTH_LTE: Float - callback1_LONGEST_LENGTH_EQUAL: Int - callback1_LONGEST_LENGTH_GT: Int - callback1_LONGEST_LENGTH_GTE: Int - callback1_LONGEST_LENGTH_LT: Int - callback1_LONGEST_LENGTH_LTE: Int - callback1_SHORTEST_LENGTH_EQUAL: Int - callback1_SHORTEST_LENGTH_GT: Int - callback1_SHORTEST_LENGTH_GTE: Int - callback1_SHORTEST_LENGTH_LT: Int - callback1_SHORTEST_LENGTH_LTE: Int - callback2_AVERAGE_LENGTH_EQUAL: Float - callback2_AVERAGE_LENGTH_GT: Float - callback2_AVERAGE_LENGTH_GTE: Float - callback2_AVERAGE_LENGTH_LT: Float - callback2_AVERAGE_LENGTH_LTE: Float - callback2_LONGEST_LENGTH_EQUAL: Int - callback2_LONGEST_LENGTH_GT: Int - callback2_LONGEST_LENGTH_GTE: Int - callback2_LONGEST_LENGTH_LT: Int - callback2_LONGEST_LENGTH_LTE: Int - callback2_SHORTEST_LENGTH_EQUAL: Int - callback2_SHORTEST_LENGTH_GT: Int - callback2_SHORTEST_LENGTH_GTE: Int - callback2_SHORTEST_LENGTH_LT: Int - callback2_SHORTEST_LENGTH_LTE: Int - callback3_AVERAGE_LENGTH_EQUAL: Float - callback3_AVERAGE_LENGTH_GT: Float - callback3_AVERAGE_LENGTH_GTE: Float - callback3_AVERAGE_LENGTH_LT: Float - callback3_AVERAGE_LENGTH_LTE: Float - callback3_LONGEST_LENGTH_EQUAL: Int - callback3_LONGEST_LENGTH_GT: Int - callback3_LONGEST_LENGTH_GTE: Int - callback3_LONGEST_LENGTH_LT: Int - callback3_LONGEST_LENGTH_LTE: Int - callback3_SHORTEST_LENGTH_EQUAL: Int - callback3_SHORTEST_LENGTH_GT: Int - callback3_SHORTEST_LENGTH_GTE: Int - callback3_SHORTEST_LENGTH_LT: Int - callback3_SHORTEST_LENGTH_LTE: Int - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + callback1: StringScalarAggregationFilters + callback1_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { averageLength: { eq: ... } } }' instead.\\") + callback1_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { averageLength: { gt: ... } } }' instead.\\") + callback1_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { averageLength: { gte: ... } } }' instead.\\") + callback1_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { averageLength: { lt: ... } } }' instead.\\") + callback1_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { averageLength: { lte: ... } } }' instead.\\") + callback1_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { longestLength: { eq: ... } } }' instead.\\") + callback1_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { longestLength: { gt: ... } } }' instead.\\") + callback1_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { longestLength: { gte: ... } } }' instead.\\") + callback1_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { longestLength: { lt: ... } } }' instead.\\") + callback1_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { longestLength: { lte: ... } } }' instead.\\") + callback1_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { shortestLength: { eq: ... } } }' instead.\\") + callback1_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { shortestLength: { gt: ... } } }' instead.\\") + callback1_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { shortestLength: { gte: ... } } }' instead.\\") + callback1_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { shortestLength: { lt: ... } } }' instead.\\") + callback1_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { shortestLength: { lte: ... } } }' instead.\\") + callback2: StringScalarAggregationFilters + callback2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { averageLength: { eq: ... } } }' instead.\\") + callback2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { averageLength: { gt: ... } } }' instead.\\") + callback2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { averageLength: { gte: ... } } }' instead.\\") + callback2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { averageLength: { lt: ... } } }' instead.\\") + callback2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { averageLength: { lte: ... } } }' instead.\\") + callback2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { longestLength: { eq: ... } } }' instead.\\") + callback2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { longestLength: { gt: ... } } }' instead.\\") + callback2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { longestLength: { gte: ... } } }' instead.\\") + callback2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { longestLength: { lt: ... } } }' instead.\\") + callback2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { longestLength: { lte: ... } } }' instead.\\") + callback2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { shortestLength: { eq: ... } } }' instead.\\") + callback2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { shortestLength: { gt: ... } } }' instead.\\") + callback2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { shortestLength: { gte: ... } } }' instead.\\") + callback2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { shortestLength: { lt: ... } } }' instead.\\") + callback2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { shortestLength: { lte: ... } } }' instead.\\") + callback3: StringScalarAggregationFilters + callback3_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { averageLength: { eq: ... } } }' instead.\\") + callback3_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { averageLength: { gt: ... } } }' instead.\\") + callback3_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { averageLength: { gte: ... } } }' instead.\\") + callback3_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { averageLength: { lt: ... } } }' instead.\\") + callback3_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { averageLength: { lte: ... } } }' instead.\\") + callback3_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { longestLength: { eq: ... } } }' instead.\\") + callback3_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { longestLength: { gt: ... } } }' instead.\\") + callback3_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { longestLength: { gte: ... } } }' instead.\\") + callback3_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { longestLength: { lt: ... } } }' instead.\\") + callback3_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { longestLength: { lte: ... } } }' instead.\\") + callback3_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { shortestLength: { eq: ... } } }' instead.\\") + callback3_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { shortestLength: { gt: ... } } }' instead.\\") + callback3_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { shortestLength: { gte: ... } } }' instead.\\") + callback3_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { shortestLength: { lt: ... } } }' instead.\\") + callback3_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { shortestLength: { lte: ... } } }' instead.\\") } input RelPropertiesCreateInput { @@ -1116,40 +1152,40 @@ describe("@populatedBy tests", () => { } input RelPropertiesUpdateInput { - callback1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - callback1_SET: String - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + callback1: StringScalarMutations + callback1_SET: String @deprecated(reason: \\"Please use the generic mutation 'callback1: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input RelPropertiesWhere { AND: [RelPropertiesWhere!] NOT: RelPropertiesWhere OR: [RelPropertiesWhere!] - callback1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback1_CONTAINS: String - callback1_ENDS_WITH: String - callback1_EQ: String - callback1_IN: [String!] - callback1_STARTS_WITH: String - callback2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback2_CONTAINS: String - callback2_ENDS_WITH: String - callback2_EQ: String - callback2_IN: [String!] - callback2_STARTS_WITH: String - callback3: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback3_CONTAINS: String - callback3_ENDS_WITH: String - callback3_EQ: String - callback3_IN: [String!] - callback3_STARTS_WITH: String - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + callback1: StringScalarFilters + callback1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { contains: ... }\\") + callback1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { endsWith: ... }\\") + callback1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { eq: ... }\\") + callback1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback1: { in: ... }\\") + callback1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback1: { startsWith: ... }\\") + callback2: StringScalarFilters + callback2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { contains: ... }\\") + callback2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { endsWith: ... }\\") + callback2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { eq: ... }\\") + callback2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback2: { in: ... }\\") + callback2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback2: { startsWith: ... }\\") + callback3: StringScalarFilters + callback3_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { contains: ... }\\") + callback3_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { endsWith: ... }\\") + callback3_EQ: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { eq: ... }\\") + callback3_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter callback3: { in: ... }\\") + callback3_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter callback3: { startsWith: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -1165,6 +1201,27 @@ describe("@populatedBy tests", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateGenresMutationResponse { genres: [Genre!]! info: UpdateInfo! @@ -1257,13 +1314,22 @@ describe("@populatedBy tests", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type Genre { id: ID! } type GenreAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input GenreConnectWhere { @@ -1279,13 +1345,15 @@ describe("@populatedBy tests", () => { node: Genre! } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] + input GenreRelationshipFilters { + \\"\\"\\"Filter type where all of the related Genres match this filter\\"\\"\\" + all: GenreWhere + \\"\\"\\"Filter type where none of the related Genres match this filter\\"\\"\\" + none: GenreWhere + \\"\\"\\"Filter type where one of the related Genres match this filter\\"\\"\\" + single: GenreWhere + \\"\\"\\"Filter type where some of the related Genres match this filter\\"\\"\\" + some: GenreWhere } \\"\\"\\" @@ -1296,20 +1364,20 @@ describe("@populatedBy tests", () => { } input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type GenresConnection { @@ -1318,9 +1386,18 @@ describe("@populatedBy tests", () => { totalCount: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -1330,16 +1407,40 @@ describe("@populatedBy tests", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - genres(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenresAggregationSelection - genresConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! + genresAggregate(where: GenreWhere): MovieGenreGenresAggregationSelection + genresConnection(after: String, first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! id: ID } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -1359,40 +1460,29 @@ describe("@populatedBy tests", () => { type MovieGenreGenresAggregationSelection { count: Int! edge: MovieGenreGenresEdgeAggregateSelection - node: MovieGenreGenresNodeAggregateSelection } type MovieGenreGenresEdgeAggregateSelection { callback1: IntAggregateSelection! callback2: IntAggregateSelection! callback3: IntAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type MovieGenreGenresNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieGenresAggregateInput { AND: [MovieGenresAggregateInput!] NOT: MovieGenresAggregateInput OR: [MovieGenresAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int edge: RelPropertiesAggregationWhereInput - node: MovieGenresNodeAggregationWhereInput } input MovieGenresConnectFieldInput { edge: RelPropertiesCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: GenreConnectWhere } @@ -1402,6 +1492,25 @@ describe("@populatedBy tests", () => { totalCount: Int! } + input MovieGenresConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieGenresConnections match this filter + \\"\\"\\" + all: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieGenresConnections match this filter + \\"\\"\\" + none: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieGenresConnections match this filter + \\"\\"\\" + single: MovieGenresConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieGenresConnections match this filter + \\"\\"\\" + some: MovieGenresConnectionWhere + } + input MovieGenresConnectionSort { edge: RelPropertiesSort node: GenreSort @@ -1433,22 +1542,6 @@ describe("@populatedBy tests", () => { create: [MovieGenresCreateFieldInput!] } - input MovieGenresNodeAggregationWhereInput { - AND: [MovieGenresNodeAggregationWhereInput!] - NOT: MovieGenresNodeAggregationWhereInput - OR: [MovieGenresNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type MovieGenresRelationship { cursor: String! node: Genre! @@ -1469,15 +1562,6 @@ describe("@populatedBy tests", () => { where: MovieGenresConnectionWhere } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1487,45 +1571,47 @@ describe("@populatedBy tests", () => { input MovieUpdateInput { genres: [MovieGenresUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + genres: GenreRelationshipFilters genresAggregate: MovieGenresAggregateInput + genresConnection: MovieGenresConnectionFilters \\"\\"\\" Return Movies where all of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_ALL: MovieGenresConnectionWhere + genresConnection_ALL: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_NONE: MovieGenresConnectionWhere + genresConnection_NONE: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_SINGLE: MovieGenresConnectionWhere + genresConnection_SINGLE: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieGenresConnections match this filter \\"\\"\\" - genresConnection_SOME: MovieGenresConnectionWhere + genresConnection_SOME: MovieGenresConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genresConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Genres match this filter\\"\\"\\" - genres_ALL: GenreWhere + genres_ALL: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Genres match this filter\\"\\"\\" - genres_NONE: GenreWhere + genres_NONE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Genres match this filter\\"\\"\\" - genres_SINGLE: GenreWhere + genres_SINGLE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Genres match this filter\\"\\"\\" - genres_SOME: GenreWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + genres_SOME: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genres: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1552,10 +1638,10 @@ describe("@populatedBy tests", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1575,76 +1661,69 @@ describe("@populatedBy tests", () => { AND: [RelPropertiesAggregationWhereInput!] NOT: RelPropertiesAggregationWhereInput OR: [RelPropertiesAggregationWhereInput!] - callback1_AVERAGE_EQUAL: Float - callback1_AVERAGE_GT: Float - callback1_AVERAGE_GTE: Float - callback1_AVERAGE_LT: Float - callback1_AVERAGE_LTE: Float - callback1_MAX_EQUAL: Int - callback1_MAX_GT: Int - callback1_MAX_GTE: Int - callback1_MAX_LT: Int - callback1_MAX_LTE: Int - callback1_MIN_EQUAL: Int - callback1_MIN_GT: Int - callback1_MIN_GTE: Int - callback1_MIN_LT: Int - callback1_MIN_LTE: Int - callback1_SUM_EQUAL: Int - callback1_SUM_GT: Int - callback1_SUM_GTE: Int - callback1_SUM_LT: Int - callback1_SUM_LTE: Int - callback2_AVERAGE_EQUAL: Float - callback2_AVERAGE_GT: Float - callback2_AVERAGE_GTE: Float - callback2_AVERAGE_LT: Float - callback2_AVERAGE_LTE: Float - callback2_MAX_EQUAL: Int - callback2_MAX_GT: Int - callback2_MAX_GTE: Int - callback2_MAX_LT: Int - callback2_MAX_LTE: Int - callback2_MIN_EQUAL: Int - callback2_MIN_GT: Int - callback2_MIN_GTE: Int - callback2_MIN_LT: Int - callback2_MIN_LTE: Int - callback2_SUM_EQUAL: Int - callback2_SUM_GT: Int - callback2_SUM_GTE: Int - callback2_SUM_LT: Int - callback2_SUM_LTE: Int - callback3_AVERAGE_EQUAL: Float - callback3_AVERAGE_GT: Float - callback3_AVERAGE_GTE: Float - callback3_AVERAGE_LT: Float - callback3_AVERAGE_LTE: Float - callback3_MAX_EQUAL: Int - callback3_MAX_GT: Int - callback3_MAX_GTE: Int - callback3_MAX_LT: Int - callback3_MAX_LTE: Int - callback3_MIN_EQUAL: Int - callback3_MIN_GT: Int - callback3_MIN_GTE: Int - callback3_MIN_LT: Int - callback3_MIN_LTE: Int - callback3_SUM_EQUAL: Int - callback3_SUM_GT: Int - callback3_SUM_GTE: Int - callback3_SUM_LT: Int - callback3_SUM_LTE: Int - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + callback1: IntScalarAggregationFilters + callback1_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { average: { eq: ... } } }' instead.\\") + callback1_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { average: { gt: ... } } }' instead.\\") + callback1_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { average: { gte: ... } } }' instead.\\") + callback1_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { average: { lt: ... } } }' instead.\\") + callback1_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { average: { lte: ... } } }' instead.\\") + callback1_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { max: { eq: ... } } }' instead.\\") + callback1_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { max: { gt: ... } } }' instead.\\") + callback1_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { max: { gte: ... } } }' instead.\\") + callback1_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { max: { lt: ... } } }' instead.\\") + callback1_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { max: { lte: ... } } }' instead.\\") + callback1_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { min: { eq: ... } } }' instead.\\") + callback1_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { min: { gt: ... } } }' instead.\\") + callback1_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { min: { gte: ... } } }' instead.\\") + callback1_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { min: { lt: ... } } }' instead.\\") + callback1_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { min: { lte: ... } } }' instead.\\") + callback1_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { sum: { eq: ... } } }' instead.\\") + callback1_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { sum: { gt: ... } } }' instead.\\") + callback1_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { sum: { gte: ... } } }' instead.\\") + callback1_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { sum: { lt: ... } } }' instead.\\") + callback1_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback1: { sum: { lte: ... } } }' instead.\\") + callback2: IntScalarAggregationFilters + callback2_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { average: { eq: ... } } }' instead.\\") + callback2_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { average: { gt: ... } } }' instead.\\") + callback2_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { average: { gte: ... } } }' instead.\\") + callback2_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { average: { lt: ... } } }' instead.\\") + callback2_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { average: { lte: ... } } }' instead.\\") + callback2_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { max: { eq: ... } } }' instead.\\") + callback2_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { max: { gt: ... } } }' instead.\\") + callback2_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { max: { gte: ... } } }' instead.\\") + callback2_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { max: { lt: ... } } }' instead.\\") + callback2_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { max: { lte: ... } } }' instead.\\") + callback2_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { min: { eq: ... } } }' instead.\\") + callback2_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { min: { gt: ... } } }' instead.\\") + callback2_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { min: { gte: ... } } }' instead.\\") + callback2_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { min: { lt: ... } } }' instead.\\") + callback2_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { min: { lte: ... } } }' instead.\\") + callback2_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { sum: { eq: ... } } }' instead.\\") + callback2_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { sum: { gt: ... } } }' instead.\\") + callback2_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { sum: { gte: ... } } }' instead.\\") + callback2_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { sum: { lt: ... } } }' instead.\\") + callback2_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback2: { sum: { lte: ... } } }' instead.\\") + callback3: IntScalarAggregationFilters + callback3_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { average: { eq: ... } } }' instead.\\") + callback3_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { average: { gt: ... } } }' instead.\\") + callback3_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { average: { gte: ... } } }' instead.\\") + callback3_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { average: { lt: ... } } }' instead.\\") + callback3_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { average: { lte: ... } } }' instead.\\") + callback3_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { max: { eq: ... } } }' instead.\\") + callback3_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { max: { gt: ... } } }' instead.\\") + callback3_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { max: { gte: ... } } }' instead.\\") + callback3_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { max: { lt: ... } } }' instead.\\") + callback3_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { max: { lte: ... } } }' instead.\\") + callback3_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { min: { eq: ... } } }' instead.\\") + callback3_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { min: { gt: ... } } }' instead.\\") + callback3_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { min: { gte: ... } } }' instead.\\") + callback3_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { min: { lt: ... } } }' instead.\\") + callback3_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { min: { lte: ... } } }' instead.\\") + callback3_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { sum: { eq: ... } } }' instead.\\") + callback3_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { sum: { gt: ... } } }' instead.\\") + callback3_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { sum: { gte: ... } } }' instead.\\") + callback3_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { sum: { lt: ... } } }' instead.\\") + callback3_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'callback3: { sum: { lte: ... } } }' instead.\\") } input RelPropertiesCreateInput { @@ -1660,45 +1739,45 @@ describe("@populatedBy tests", () => { } input RelPropertiesUpdateInput { - callback1: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - callback1_DECREMENT: Int - callback1_INCREMENT: Int - callback1_SET: Int - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + callback1: IntScalarMutations + callback1_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'callback1: { decrement: ... } }' instead.\\") + callback1_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'callback1: { increment: ... } }' instead.\\") + callback1_SET: Int @deprecated(reason: \\"Please use the generic mutation 'callback1: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input RelPropertiesWhere { AND: [RelPropertiesWhere!] NOT: RelPropertiesWhere OR: [RelPropertiesWhere!] - callback1: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback1_EQ: Int - callback1_GT: Int - callback1_GTE: Int - callback1_IN: [Int!] - callback1_LT: Int - callback1_LTE: Int - callback2: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback2_EQ: Int - callback2_GT: Int - callback2_GTE: Int - callback2_IN: [Int!] - callback2_LT: Int - callback2_LTE: Int - callback3: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - callback3_EQ: Int - callback3_GT: Int - callback3_GTE: Int - callback3_IN: [Int!] - callback3_LT: Int - callback3_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + callback1: IntScalarFilters + callback1_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { eq: ... }\\") + callback1_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { gt: ... }\\") + callback1_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { gte: ... }\\") + callback1_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback1: { in: ... }\\") + callback1_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { lt: ... }\\") + callback1_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback1: { lte: ... }\\") + callback2: IntScalarFilters + callback2_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { eq: ... }\\") + callback2_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { gt: ... }\\") + callback2_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { gte: ... }\\") + callback2_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback2: { in: ... }\\") + callback2_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { lt: ... }\\") + callback2_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback2: { lte: ... }\\") + callback3: IntScalarFilters + callback3_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { eq: ... }\\") + callback3_GT: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { gt: ... }\\") + callback3_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { gte: ... }\\") + callback3_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter callback3: { in: ... }\\") + callback3_LT: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { lt: ... }\\") + callback3_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter callback3: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" diff --git a/packages/graphql/tests/schema/directives/private.test.ts b/packages/graphql/tests/schema/directives/private.test.ts deleted file mode 100644 index b8642c55ec..0000000000 --- a/packages/graphql/tests/schema/directives/private.test.ts +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("@private directive", () => { - test("does not add fields to schema", async () => { - const typeDefs = gql` - interface UserInterface { - id: ID - private: String @private - } - - type User implements UserInterface @node { - id: ID - password: String @private - private: String @private - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - userInterfaces(limit: Int, offset: Int, options: UserInterfaceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! - userInterfacesAggregate(where: UserInterfaceWhere): UserInterfaceAggregateSelection! - userInterfacesConnection(after: String, first: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): UserInterfacesConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User implements UserInterface { - id: ID - } - - type UserAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input UserCreateInput { - id: ID - } - - type UserEdge { - cursor: String! - node: User! - } - - interface UserInterface { - id: ID - } - - type UserInterfaceAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type UserInterfaceEdge { - cursor: String! - node: UserInterface! - } - - enum UserInterfaceImplementation { - User - } - - input UserInterfaceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserInterfaceSort objects to sort UserInterfaces by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserInterfaceSort!] - } - - \\"\\"\\" - Fields to sort UserInterfaces by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserInterfaceSort object. - \\"\\"\\" - input UserInterfaceSort { - id: SortDirection - } - - input UserInterfaceWhere { - AND: [UserInterfaceWhere!] - NOT: UserInterfaceWhere - OR: [UserInterfaceWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - typename: [UserInterfaceImplementation!] - typename_IN: [UserInterfaceImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type UserInterfacesConnection { - edges: [UserInterfaceEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - id: SortDirection - } - - input UserUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); - - test("private is not inherited", async () => { - const typeDefs = gql` - interface UserInterface { - id: ID - private: String @private - } - - type User implements UserInterface @node { - id: ID - password: String @private - private: String - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - userInterfaces(limit: Int, offset: Int, options: UserInterfaceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserInterfaceSort!], where: UserInterfaceWhere): [UserInterface!]! - userInterfacesAggregate(where: UserInterfaceWhere): UserInterfaceAggregateSelection! - userInterfacesConnection(after: String, first: Int, sort: [UserInterfaceSort!], where: UserInterfaceWhere): UserInterfacesConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User implements UserInterface { - id: ID - private: String - } - - type UserAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - private: StringAggregateSelection! - } - - input UserCreateInput { - id: ID - private: String - } - - type UserEdge { - cursor: String! - node: User! - } - - interface UserInterface { - id: ID - } - - type UserInterfaceAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type UserInterfaceEdge { - cursor: String! - node: UserInterface! - } - - enum UserInterfaceImplementation { - User - } - - input UserInterfaceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserInterfaceSort objects to sort UserInterfaces by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserInterfaceSort!] - } - - \\"\\"\\" - Fields to sort UserInterfaces by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserInterfaceSort object. - \\"\\"\\" - input UserInterfaceSort { - id: SortDirection - } - - input UserInterfaceWhere { - AND: [UserInterfaceWhere!] - NOT: UserInterfaceWhere - OR: [UserInterfaceWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - typename: [UserInterfaceImplementation!] - typename_IN: [UserInterfaceImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type UserInterfacesConnection { - edges: [UserInterfaceEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - id: SortDirection - private: SortDirection - } - - input UserUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - private: String @deprecated(reason: \\"Please use the explicit _SET field\\") - private_SET: String - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - private: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - private_CONTAINS: String - private_ENDS_WITH: String - private_EQ: String - private_IN: [String] - private_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/directives/relationship-aggregate.test.ts b/packages/graphql/tests/schema/directives/relationship-aggregate.test.ts index 7567cc23ea..2591cd864e 100644 --- a/packages/graphql/tests/schema/directives/relationship-aggregate.test.ts +++ b/packages/graphql/tests/schema/directives/relationship-aggregate.test.ts @@ -252,13 +252,15 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -270,28 +272,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -326,9 +328,29 @@ describe("@relationship directive, aggregate argument", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -336,7 +358,7 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -346,10 +368,6 @@ describe("@relationship directive, aggregate argument", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -359,6 +377,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -391,36 +428,38 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -460,15 +499,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -478,45 +508,47 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -543,10 +575,10 @@ describe("@relationship directive, aggregate argument", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -564,6 +596,27 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -633,13 +686,15 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -651,28 +706,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -707,10 +762,30 @@ describe("@relationship directive, aggregate argument", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -728,7 +803,7 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -738,10 +813,6 @@ describe("@relationship directive, aggregate argument", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -751,6 +822,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -783,36 +873,38 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -852,15 +944,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -870,45 +953,47 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -935,10 +1020,10 @@ describe("@relationship directive, aggregate argument", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -956,6 +1041,27 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1027,15 +1133,6 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1045,28 +1142,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -1101,9 +1198,29 @@ describe("@relationship directive, aggregate argument", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -1111,7 +1228,7 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1130,6 +1247,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -1162,36 +1298,38 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1231,15 +1369,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1249,45 +1378,47 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1347,13 +1478,15 @@ describe("@relationship directive, aggregate argument", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -1365,40 +1498,39 @@ describe("@relationship directive, aggregate argument", () => { } input PersonUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1416,6 +1548,27 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1485,15 +1638,6 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1503,28 +1647,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -1559,10 +1703,30 @@ describe("@relationship directive, aggregate argument", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -1570,7 +1734,7 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1589,6 +1753,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -1621,36 +1804,38 @@ describe("@relationship directive, aggregate argument", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - password_AVERAGE_LENGTH_EQUAL: Float - password_AVERAGE_LENGTH_GT: Float - password_AVERAGE_LENGTH_GTE: Float - password_AVERAGE_LENGTH_LT: Float - password_AVERAGE_LENGTH_LTE: Float - password_LONGEST_LENGTH_EQUAL: Int - password_LONGEST_LENGTH_GT: Int - password_LONGEST_LENGTH_GTE: Int - password_LONGEST_LENGTH_LT: Int - password_LONGEST_LENGTH_LTE: Int - password_SHORTEST_LENGTH_EQUAL: Int - password_SHORTEST_LENGTH_GT: Int - password_SHORTEST_LENGTH_GTE: Int - password_SHORTEST_LENGTH_LT: Int - password_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int + password: StringScalarAggregationFilters + password_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { eq: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { gte: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lt: ... } } }' instead.\\") + password_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'password: { averageLength: { lte: ... } } }' instead.\\") + password_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { eq: ... } } }' instead.\\") + password_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gt: ... } } }' instead.\\") + password_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { gte: ... } } }' instead.\\") + password_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lt: ... } } }' instead.\\") + password_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { longestLength: { lte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { eq: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { gte: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lt: ... } } }' instead.\\") + password_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'password: { shortestLength: { lte: ... } } }' instead.\\") + username: StringScalarAggregationFilters + username_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { eq: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { gte: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lt: ... } } }' instead.\\") + username_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'username: { averageLength: { lte: ... } } }' instead.\\") + username_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { eq: ... } } }' instead.\\") + username_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gt: ... } } }' instead.\\") + username_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { gte: ... } } }' instead.\\") + username_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lt: ... } } }' instead.\\") + username_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { longestLength: { lte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { eq: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { gte: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lt: ... } } }' instead.\\") + username_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'username: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1690,15 +1875,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -1718,45 +1894,47 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1816,13 +1994,15 @@ describe("@relationship directive, aggregate argument", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -1834,40 +2014,39 @@ describe("@relationship directive, aggregate argument", () => { } input PersonUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1885,6 +2064,27 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1962,15 +2162,6 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1980,28 +2171,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -2012,6 +2203,17 @@ describe("@relationship directive, aggregate argument", () => { union CastMember = Actor | Person + input CastMemberRelationshipFilters { + \\"\\"\\"Filter type where all of the related CastMembers match this filter\\"\\"\\" + all: CastMemberWhere + \\"\\"\\"Filter type where none of the related CastMembers match this filter\\"\\"\\" + none: CastMemberWhere + \\"\\"\\"Filter type where one of the related CastMembers match this filter\\"\\"\\" + single: CastMemberWhere + \\"\\"\\"Filter type where some of the related CastMembers match this filter\\"\\"\\" + some: CastMemberWhere + } + input CastMemberWhere { Actor: ActorWhere Person: PersonWhere @@ -2049,8 +2251,8 @@ describe("@relationship directive, aggregate argument", () => { } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CastMemberWhere): [CastMember!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: CastMemberWhere): [CastMember!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -2101,6 +2303,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Actor: MovieActorsActorConnectionWhere Person: MovieActorsPersonConnectionWhere @@ -2186,15 +2407,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2204,44 +2416,46 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: CastMemberRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related CastMembers match this filter\\"\\"\\" - actors_ALL: CastMemberWhere + actors_ALL: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related CastMembers match this filter\\"\\"\\" - actors_NONE: CastMemberWhere + actors_NONE: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related CastMembers match this filter\\"\\"\\" - actors_SINGLE: CastMemberWhere + actors_SINGLE: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related CastMembers match this filter\\"\\"\\" - actors_SOME: CastMemberWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2298,15 +2512,6 @@ describe("@relationship directive, aggregate argument", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - \\"\\"\\" Fields to sort People by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonSort object. \\"\\"\\" @@ -2315,41 +2520,35 @@ describe("@relationship directive, aggregate argument", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - castMembers(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CastMemberWhere): [CastMember!]! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + castMembers(limit: Int, offset: Int, where: CastMemberWhere): [CastMember!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -2363,6 +2562,20 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2442,15 +2655,6 @@ describe("@relationship directive, aggregate argument", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -2460,28 +2664,28 @@ describe("@relationship directive, aggregate argument", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -2492,6 +2696,17 @@ describe("@relationship directive, aggregate argument", () => { union CastMember = Actor | Person + input CastMemberRelationshipFilters { + \\"\\"\\"Filter type where all of the related CastMembers match this filter\\"\\"\\" + all: CastMemberWhere + \\"\\"\\"Filter type where none of the related CastMembers match this filter\\"\\"\\" + none: CastMemberWhere + \\"\\"\\"Filter type where one of the related CastMembers match this filter\\"\\"\\" + single: CastMemberWhere + \\"\\"\\"Filter type where some of the related CastMembers match this filter\\"\\"\\" + some: CastMemberWhere + } + input CastMemberWhere { Actor: ActorWhere Person: PersonWhere @@ -2529,8 +2744,8 @@ describe("@relationship directive, aggregate argument", () => { } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CastMemberWhere): [CastMember!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: CastMemberWhere): [CastMember!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -2581,6 +2796,25 @@ describe("@relationship directive, aggregate argument", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Actor: MovieActorsActorConnectionWhere Person: MovieActorsPersonConnectionWhere @@ -2666,15 +2900,6 @@ describe("@relationship directive, aggregate argument", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2684,44 +2909,46 @@ describe("@relationship directive, aggregate argument", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: CastMemberRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related CastMembers match this filter\\"\\"\\" - actors_ALL: CastMemberWhere + actors_ALL: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related CastMembers match this filter\\"\\"\\" - actors_NONE: CastMemberWhere + actors_NONE: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related CastMembers match this filter\\"\\"\\" - actors_SINGLE: CastMemberWhere + actors_SINGLE: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related CastMembers match this filter\\"\\"\\" - actors_SOME: CastMemberWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + actors_SOME: CastMemberWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2778,15 +3005,6 @@ describe("@relationship directive, aggregate argument", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - \\"\\"\\" Fields to sort People by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonSort object. \\"\\"\\" @@ -2795,41 +3013,35 @@ describe("@relationship directive, aggregate argument", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - castMembers(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CastMemberWhere): [CastMember!]! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + castMembers(limit: Int, offset: Int, where: CastMemberWhere): [CastMember!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -2843,6 +3055,20 @@ describe("@relationship directive, aggregate argument", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts b/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts index 4c4fcbb000..34c60e646d 100644 --- a/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts +++ b/packages/graphql/tests/schema/directives/relationship-nested-operations.test.ts @@ -76,15 +76,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -92,7 +121,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -107,6 +136,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -122,21 +170,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -146,7 +195,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -158,15 +206,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -184,45 +223,47 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -272,13 +313,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -289,27 +332,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -327,6 +370,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -395,15 +459,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -411,7 +504,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -426,6 +519,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -449,21 +561,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -478,7 +591,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -491,15 +603,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -518,45 +621,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -606,13 +711,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -623,27 +730,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -661,6 +768,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -729,15 +857,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -745,7 +902,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -755,10 +912,6 @@ describe("Relationship nested operations", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PersonConnectWhere } @@ -768,6 +921,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -787,21 +959,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -816,7 +989,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -829,15 +1001,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -856,45 +1019,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -948,13 +1113,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -965,27 +1132,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1003,6 +1170,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -1071,15 +1259,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -1087,7 +1304,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1102,6 +1319,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -1117,21 +1353,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1150,7 +1387,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -1162,15 +1398,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -1189,45 +1416,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1277,13 +1506,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -1294,27 +1525,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1332,6 +1563,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -1400,15 +1652,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -1416,7 +1697,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1431,6 +1712,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -1450,21 +1750,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1479,7 +1780,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -1495,15 +1795,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -1522,45 +1813,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1610,13 +1903,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -1627,27 +1922,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1665,6 +1960,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -1733,15 +2049,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -1749,7 +2094,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1764,6 +2109,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -1783,21 +2147,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1812,7 +2177,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -1824,15 +2188,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -1851,45 +2206,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -1939,13 +2296,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -1956,27 +2315,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -1994,6 +2353,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -2063,15 +2443,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -2079,7 +2488,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2094,6 +2503,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -2109,21 +2537,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2133,7 +2562,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -2145,15 +2573,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -2171,45 +2590,47 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -2259,13 +2680,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -2276,27 +2699,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -2314,6 +2737,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -2339,7 +2783,7 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with nested operation CONNECT_OR_CREATE specified", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -2384,15 +2828,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -2400,7 +2873,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2409,21 +2882,31 @@ describe("Relationship nested operations", () => { node: MovieActorsNodeAggregationWhereInput } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - node: PersonOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -2435,39 +2918,26 @@ describe("Relationship nested operations", () => { node: PersonWhere } - input MovieActorsFieldInput { - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - input MovieActorsNodeAggregationWhereInput { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2475,18 +2945,11 @@ describe("Relationship nested operations", () => { node: Person! } - input MovieActorsUpdateFieldInput { - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] - where: MovieActorsConnectionWhere - } - type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { - actors: MovieActorsFieldInput id: ID } @@ -2495,22 +2958,12 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection } type MoviePersonActorsNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -2522,46 +2975,47 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -2600,14 +3054,9 @@ describe("Relationship nested operations", () => { type PersonAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonCreateInput { name: String } @@ -2617,17 +3066,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOnCreateInput { - name: String - } - - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -2638,39 +3085,34 @@ describe("Relationship nested operations", () => { name: SortDirection } - input PersonUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -2688,6 +3130,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -2757,26 +3220,55 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - producersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonProducersAggregationSelection - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + producersAggregate(where: PersonWhere): MoviePersonProducersAggregationSelection + producersConnection(after: String, first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! } input MovieActorsAggregateInput { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2786,10 +3278,6 @@ describe("Relationship nested operations", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PersonConnectWhere } @@ -2799,6 +3287,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -2831,21 +3338,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2868,7 +3376,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -2885,15 +3392,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -2916,7 +3414,7 @@ describe("Relationship nested operations", () => { AND: [MovieProducersAggregateInput!] NOT: MovieProducersAggregateInput OR: [MovieProducersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2931,6 +3429,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionSort { node: PersonSort } @@ -2950,21 +3467,22 @@ describe("Relationship nested operations", () => { AND: [MovieProducersNodeAggregationWhereInput!] NOT: MovieProducersNodeAggregationWhereInput OR: [MovieProducersNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieProducersRelationship { @@ -2986,8 +3504,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: [MovieProducersUpdateFieldInput!] } @@ -2995,62 +3513,66 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters producersAggregate: MovieProducersAggregateInput + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -3104,13 +3626,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -3121,27 +3645,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -3159,6 +3683,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -3228,26 +3773,55 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - producersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonProducersAggregationSelection - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + producersAggregate(where: PersonWhere): MoviePersonProducersAggregationSelection + producersConnection(after: String, first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! } input MovieActorsAggregateInput { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3262,6 +3836,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -3285,21 +3878,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -3314,7 +3908,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -3327,15 +3920,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -3358,7 +3942,7 @@ describe("Relationship nested operations", () => { AND: [MovieProducersAggregateInput!] NOT: MovieProducersAggregateInput OR: [MovieProducersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3373,6 +3957,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionSort { node: PersonSort } @@ -3392,21 +3995,22 @@ describe("Relationship nested operations", () => { AND: [MovieProducersNodeAggregationWhereInput!] NOT: MovieProducersNodeAggregationWhereInput OR: [MovieProducersNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieProducersRelationship { @@ -3428,8 +4032,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: [MovieProducersUpdateFieldInput!] } @@ -3437,62 +4041,66 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters producersAggregate: MovieProducersAggregateInput + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -3542,13 +4150,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -3559,27 +4169,27 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -3597,6 +4207,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -3683,14 +4314,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -3700,6 +4340,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -3726,7 +4385,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -3738,15 +4396,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3755,44 +4404,46 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -3841,15 +4492,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -3858,20 +4500,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -3880,6 +4522,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -3898,15 +4551,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -3915,20 +4559,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -3943,24 +4587,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -3974,6 +4612,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -4058,14 +4710,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -4075,6 +4736,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -4137,7 +4817,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -4150,15 +4829,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -4168,44 +4838,46 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -4254,15 +4926,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -4271,20 +4934,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -4293,6 +4956,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -4311,15 +4985,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -4328,20 +4993,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -4356,24 +5021,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -4387,6 +5046,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -4471,14 +5144,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -4488,6 +5170,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -4550,7 +5251,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -4563,15 +5263,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -4581,44 +5272,46 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -4671,15 +5364,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -4688,20 +5372,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -4710,6 +5394,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -4732,15 +5427,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -4749,20 +5435,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -4777,24 +5463,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -4808,6 +5488,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -4892,14 +5586,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -4909,6 +5612,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -4958,7 +5680,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -4970,15 +5691,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -4988,44 +5700,46 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -5074,15 +5788,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -5091,20 +5796,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -5113,6 +5818,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -5131,15 +5847,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -5148,20 +5855,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -5176,24 +5883,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -5207,6 +5908,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -5291,14 +6006,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -5308,6 +6032,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -5362,7 +6105,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -5378,15 +6120,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -5396,44 +6129,46 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -5482,15 +6217,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -5499,20 +6225,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -5521,6 +6247,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -5539,15 +6276,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -5556,20 +6284,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -5584,24 +6312,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -5615,6 +6337,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -5699,14 +6435,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -5716,6 +6461,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -5765,7 +6529,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -5777,15 +6540,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -5795,44 +6549,46 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -5881,15 +6637,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -5898,20 +6645,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -5920,6 +6667,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -5938,15 +6696,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -5955,20 +6704,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -5983,24 +6732,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -6014,6 +6757,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -6099,14 +6856,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -6116,6 +6882,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -6142,7 +6927,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -6154,15 +6938,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -6171,44 +6946,46 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -6257,15 +7034,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -6274,20 +7042,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -6296,6 +7064,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -6314,15 +7093,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -6331,20 +7101,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -6359,24 +7129,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -6390,6 +7154,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -6420,12 +7198,12 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with nested operation CONNECT_OR_CREATE specified", async () => { const typeDefs = gql` type PersonOne @node { - id: ID! @id @unique + id: ID! @id name: String } type PersonTwo @node { - id: ID! @id @unique + id: ID! @id nameTwo: String } @@ -6477,14 +7255,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -6494,25 +7281,30 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere } - input MovieActorsCreateInput { - PersonOne: MovieActorsPersonOneFieldInput - PersonTwo: MovieActorsPersonTwoFieldInput - } - - input MovieActorsPersonOneConnectOrCreateFieldInput { - onCreate: MovieActorsPersonOneConnectOrCreateFieldInputOnCreate! - where: PersonOneConnectOrCreateWhere! - } - - input MovieActorsPersonOneConnectOrCreateFieldInputOnCreate { - node: PersonOneOnCreateInput! - } - input MovieActorsPersonOneConnectionWhere { AND: [MovieActorsPersonOneConnectionWhere!] NOT: MovieActorsPersonOneConnectionWhere @@ -6520,24 +7312,6 @@ describe("Relationship nested operations", () => { node: PersonOneWhere } - input MovieActorsPersonOneFieldInput { - connectOrCreate: [MovieActorsPersonOneConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - - input MovieActorsPersonOneUpdateFieldInput { - connectOrCreate: [MovieActorsPersonOneConnectOrCreateFieldInput!] - where: MovieActorsPersonOneConnectionWhere - } - - input MovieActorsPersonTwoConnectOrCreateFieldInput { - onCreate: MovieActorsPersonTwoConnectOrCreateFieldInputOnCreate! - where: PersonTwoConnectOrCreateWhere! - } - - input MovieActorsPersonTwoConnectOrCreateFieldInputOnCreate { - node: PersonTwoOnCreateInput! - } - input MovieActorsPersonTwoConnectionWhere { AND: [MovieActorsPersonTwoConnectionWhere!] NOT: MovieActorsPersonTwoConnectionWhere @@ -6545,32 +7319,16 @@ describe("Relationship nested operations", () => { node: PersonTwoWhere } - input MovieActorsPersonTwoFieldInput { - connectOrCreate: [MovieActorsPersonTwoConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - } - - input MovieActorsPersonTwoUpdateFieldInput { - connectOrCreate: [MovieActorsPersonTwoConnectOrCreateFieldInput!] - where: MovieActorsPersonTwoConnectionWhere - } - type MovieActorsRelationship { cursor: String! node: Person! } - input MovieActorsUpdateInput { - PersonOne: [MovieActorsPersonOneUpdateFieldInput!] - PersonTwo: [MovieActorsPersonTwoUpdateFieldInput!] - } - type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { - actors: MovieActorsCreateInput id: ID } @@ -6579,15 +7337,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -6596,45 +7345,46 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -6672,14 +7422,9 @@ describe("Relationship nested operations", () => { type PersonOneAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } - input PersonOneConnectOrCreateWhere { - node: PersonOneUniqueWhere! - } - input PersonOneCreateInput { name: String } @@ -6689,19 +7434,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOnCreateInput { - name: String - } - - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -6710,32 +7442,27 @@ describe("Relationship nested operations", () => { name: SortDirection } - input PersonOneUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -6744,6 +7471,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { id: ID! nameTwo: String @@ -6751,14 +7489,9 @@ describe("Relationship nested operations", () => { type PersonTwoAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") nameTwo: StringAggregateSelection! } - input PersonTwoConnectOrCreateWhere { - node: PersonTwoUniqueWhere! - } - input PersonTwoCreateInput { nameTwo: String } @@ -6768,19 +7501,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOnCreateInput { - nameTwo: String - } - - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -6789,32 +7509,27 @@ describe("Relationship nested operations", () => { nameTwo: SortDirection } - input PersonTwoUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -6829,24 +7544,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -6860,6 +7569,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -6945,17 +7668,26 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, where: PersonWhere): [Person!]! + producersConnection(after: String, first: Int, where: MovieProducersConnectionWhere): MovieProducersConnection! } type MovieActorsConnection { @@ -6964,6 +7696,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -7073,7 +7824,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -7090,21 +7840,31 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MovieProducersConnection { edges: [MovieProducersRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionWhere { PersonOne: MovieProducersPersonOneConnectionWhere PersonTwo: MovieProducersPersonTwoConnectionWhere @@ -7161,8 +7921,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: MovieProducersUpdateInput } @@ -7170,60 +7930,64 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -7276,15 +8040,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -7293,20 +8048,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -7315,6 +8070,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -7337,15 +8103,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -7354,20 +8111,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -7382,24 +8139,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -7413,6 +8164,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -7498,17 +8263,26 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, where: PersonWhere): [Person!]! + producersConnection(after: String, first: Int, where: MovieProducersConnectionWhere): MovieProducersConnection! } type MovieActorsConnection { @@ -7517,6 +8291,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -7579,7 +8372,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -7592,21 +8384,31 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MovieProducersConnection { edges: [MovieProducersRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionWhere { PersonOne: MovieProducersPersonOneConnectionWhere PersonTwo: MovieProducersPersonTwoConnectionWhere @@ -7663,8 +8465,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: MovieActorsUpdateInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: MovieProducersUpdateInput } @@ -7672,60 +8474,64 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -7774,15 +8580,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -7791,20 +8588,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -7813,6 +8610,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -7831,15 +8639,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -7848,20 +8647,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -7876,24 +8675,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -7907,6 +8700,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -8001,15 +8808,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -8017,7 +8866,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8032,6 +8881,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -8047,21 +8915,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -8071,7 +8940,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -8083,15 +8951,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -8109,45 +8968,47 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -8221,15 +9082,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -8238,27 +9090,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -8267,13 +9119,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -8301,15 +9155,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -8318,20 +9163,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -8344,27 +9189,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -8382,6 +9226,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -8469,15 +9334,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -8485,7 +9392,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8500,6 +9407,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -8523,21 +9449,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -8552,7 +9479,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -8565,15 +9491,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -8592,45 +9509,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -8709,15 +9628,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -8726,27 +9636,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -8755,13 +9665,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -8789,15 +9701,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -8806,20 +9709,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -8832,27 +9735,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -8870,6 +9772,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -8957,15 +9880,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -8973,7 +9938,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8992,6 +9957,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -9011,21 +9995,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -9040,7 +10025,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -9053,15 +10037,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -9080,45 +10055,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -9196,15 +10173,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -9213,27 +10181,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -9242,13 +10210,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -9272,17 +10242,8 @@ describe("Relationship nested operations", () => { } type PersonTwoEdge { - cursor: String! - node: PersonTwo! - } - - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] + cursor: String! + node: PersonTwo! } \\"\\"\\" @@ -9293,20 +10254,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -9319,27 +10280,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -9357,6 +10317,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -9444,15 +10425,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -9460,7 +10483,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9475,6 +10498,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -9490,21 +10532,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -9523,7 +10566,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -9535,15 +10577,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -9562,45 +10595,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -9674,15 +10709,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -9691,27 +10717,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -9720,13 +10746,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -9754,15 +10782,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -9771,20 +10790,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -9794,35 +10813,34 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -9840,6 +10858,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -9927,15 +10966,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -9943,7 +11024,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9958,6 +11039,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -9977,21 +11077,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -10006,7 +11107,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -10022,15 +11122,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -10049,45 +11140,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -10161,15 +11254,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -10178,27 +11262,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -10207,13 +11291,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -10241,15 +11327,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -10258,20 +11335,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -10284,27 +11361,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -10322,6 +11398,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -10409,15 +11506,57 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -10425,7 +11564,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -10440,6 +11579,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -10459,21 +11617,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -10488,7 +11647,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -10500,15 +11658,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -10527,45 +11676,47 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -10639,15 +11790,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -10656,27 +11798,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -10685,13 +11827,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -10719,15 +11863,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -10736,20 +11871,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -10762,27 +11897,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -10800,6 +11934,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -10888,26 +12043,68 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - producersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonProducersAggregationSelection - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + producersAggregate(where: PersonWhere): MoviePersonProducersAggregationSelection + producersConnection(after: String, first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! } input MovieActorsAggregateInput { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -10926,6 +12123,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -10958,21 +12174,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -10995,7 +12212,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -11012,15 +12228,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -11043,7 +12250,7 @@ describe("Relationship nested operations", () => { AND: [MovieProducersAggregateInput!] NOT: MovieProducersAggregateInput OR: [MovieProducersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -11058,6 +12265,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionSort { node: PersonSort } @@ -11077,21 +12303,22 @@ describe("Relationship nested operations", () => { AND: [MovieProducersNodeAggregationWhereInput!] NOT: MovieProducersNodeAggregationWhereInput OR: [MovieProducersNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieProducersRelationship { @@ -11113,8 +12340,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: [MovieProducersUpdateFieldInput!] } @@ -11122,62 +12349,66 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters producersAggregate: MovieProducersAggregateInput + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -11260,15 +12491,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -11277,27 +12499,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -11306,13 +12528,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -11340,15 +12564,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -11357,20 +12572,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -11380,35 +12595,34 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -11426,6 +12640,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -11515,26 +12750,68 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID - producers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - producersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonProducersAggregationSelection - producersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! + producers(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + producersAggregate(where: PersonWhere): MoviePersonProducersAggregationSelection + producersConnection(after: String, first: Int, sort: [MovieProducersConnectionSort!], where: MovieProducersConnectionWhere): MovieProducersConnection! } input MovieActorsAggregateInput { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -11549,6 +12826,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -11576,21 +12872,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -11606,7 +12903,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -11623,15 +12919,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection @@ -11654,7 +12941,7 @@ describe("Relationship nested operations", () => { AND: [MovieProducersAggregateInput!] NOT: MovieProducersAggregateInput OR: [MovieProducersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -11669,6 +12956,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieProducersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieProducersConnections match this filter + \\"\\"\\" + all: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieProducersConnections match this filter + \\"\\"\\" + none: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieProducersConnections match this filter + \\"\\"\\" + single: MovieProducersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieProducersConnections match this filter + \\"\\"\\" + some: MovieProducersConnectionWhere + } + input MovieProducersConnectionSort { node: PersonSort } @@ -11688,21 +12994,22 @@ describe("Relationship nested operations", () => { AND: [MovieProducersNodeAggregationWhereInput!] NOT: MovieProducersNodeAggregationWhereInput OR: [MovieProducersNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieProducersRelationship { @@ -11724,8 +13031,8 @@ describe("Relationship nested operations", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") producers: [MovieProducersUpdateFieldInput!] } @@ -11733,62 +13040,66 @@ describe("Relationship nested operations", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + producers: PersonRelationshipFilters producersAggregate: MovieProducersAggregateInput + producersConnection: MovieProducersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_ALL: MovieProducersConnectionWhere + producersConnection_ALL: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_NONE: MovieProducersConnectionWhere + producersConnection_NONE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SINGLE: MovieProducersConnectionWhere + producersConnection_SINGLE: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieProducersConnections match this filter \\"\\"\\" - producersConnection_SOME: MovieProducersConnectionWhere + producersConnection_SOME: MovieProducersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'producersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - producers_ALL: PersonWhere + producers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - producers_NONE: PersonWhere + producers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - producers_SINGLE: PersonWhere + producers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - producers_SOME: PersonWhere + producers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'producers: { some: ... }' instead.\\") } type MoviesConnection { @@ -11867,15 +13178,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -11884,27 +13186,27 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - someExtraProp_POP: Int - someExtraProp_PUSH: [Int!] - someExtraProp_SET: [Int!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + someExtraProp: ListIntMutations + someExtraProp_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { pop: ... } }' instead.\\") + someExtraProp_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { push: ... } }' instead.\\") + someExtraProp_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someExtraProp: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - someExtraProp: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - someExtraProp_EQ: [Int!] - someExtraProp_INCLUDES: Int + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + someExtraProp: IntListFilters + someExtraProp_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { eq: ... }\\") + someExtraProp_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someExtraProp: { includes: ... }\\") } type PersonOnesConnection { @@ -11913,13 +13215,15 @@ describe("Relationship nested operations", () => { totalCount: Int! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -11947,15 +13251,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -11964,20 +13259,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonTwosConnection { @@ -11990,27 +13285,26 @@ describe("Relationship nested operations", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } @@ -12028,6 +13322,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/directives/relationship-properties.test.ts b/packages/graphql/tests/schema/directives/relationship-properties.test.ts index 57d687d396..61605dd344 100644 --- a/packages/graphql/tests/schema/directives/relationship-properties.test.ts +++ b/packages/graphql/tests/schema/directives/relationship-properties.test.ts @@ -65,26 +65,27 @@ describe("Relationship-properties", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -100,42 +101,42 @@ describe("Relationship-properties", () => { } input ActedInUpdateInput { - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - leadRole_SET: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _SET field\\") - startDate_SET: Date + leadRole: BooleanScalarMutations + leadRole_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'leadRole: { set: ... } }' instead.\\") + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + startDate: DateScalarMutations + startDate_SET: Date @deprecated(reason: \\"Please use the generic mutation 'startDate: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - leadRole_EQ: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _EQ version\\") - startDate_EQ: Date - startDate_GT: Date - startDate_GTE: Date - startDate_IN: [Date!] - startDate_LT: Date - startDate_LTE: Date + leadRole: BooleanScalarFilters + leadRole_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter leadRole: { eq: ... }\\") + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + startDate: DateScalarFilters + startDate_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter startDate: { eq: ... }\\") + startDate_GT: Date @deprecated(reason: \\"Please use the relevant generic filter startDate: { gt: ... }\\") + startDate_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter startDate: { gte: ... }\\") + startDate_IN: [Date!] @deprecated(reason: \\"Please use the relevant generic filter startDate: { in: ... }\\") + startDate_LT: Date @deprecated(reason: \\"Please use the relevant generic filter startDate: { lt: ... }\\") + startDate_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter startDate: { lte: ... }\\") } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } @@ -188,7 +189,7 @@ describe("Relationship-properties", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -201,10 +202,6 @@ describe("Relationship-properties", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -214,6 +211,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -251,21 +267,22 @@ describe("Relationship-properties", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -288,13 +305,15 @@ describe("Relationship-properties", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -306,45 +325,47 @@ describe("Relationship-properties", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -353,6 +374,16 @@ describe("Relationship-properties", () => { totalCount: Int! } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -374,6 +405,21 @@ describe("Relationship-properties", () => { \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" scalar Date + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + + \\"\\"\\"Date mutations\\"\\"\\" + input DateScalarMutations { + set: Date + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -382,6 +428,16 @@ describe("Relationship-properties", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -389,10 +445,35 @@ describe("Relationship-properties", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -414,7 +495,7 @@ describe("Relationship-properties", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -427,10 +508,6 @@ describe("Relationship-properties", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -440,6 +517,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -477,21 +573,22 @@ describe("Relationship-properties", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -545,13 +642,15 @@ describe("Relationship-properties", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -563,45 +662,47 @@ describe("Relationship-properties", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -628,10 +729,10 @@ describe("Relationship-properties", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -649,6 +750,27 @@ describe("Relationship-properties", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -713,46 +835,38 @@ describe("Relationship-properties", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int - timestamp_MAX_EQUAL: DateTime - timestamp_MAX_GT: DateTime - timestamp_MAX_GTE: DateTime - timestamp_MAX_LT: DateTime - timestamp_MAX_LTE: DateTime - timestamp_MIN_EQUAL: DateTime - timestamp_MIN_GT: DateTime - timestamp_MIN_GTE: DateTime - timestamp_MIN_LT: DateTime - timestamp_MIN_LTE: DateTime + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") + timestamp: DateTimeScalarAggregationFilters + timestamp_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { eq: ... } } }' instead.\\") + timestamp_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { gt: ... } } }' instead.\\") + timestamp_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { gte: ... } } }' instead.\\") + timestamp_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { lt: ... } } }' instead.\\") + timestamp_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { lte: ... } } }' instead.\\") + timestamp_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { eq: ... } } }' instead.\\") + timestamp_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { gt: ... } } }' instead.\\") + timestamp_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { gte: ... } } }' instead.\\") + timestamp_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { lt: ... } } }' instead.\\") + timestamp_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -766,42 +880,42 @@ describe("Relationship-properties", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - timestamp: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - timestamp_EQ: DateTime - timestamp_GT: DateTime - timestamp_GTE: DateTime - timestamp_IN: [DateTime!] - timestamp_LT: DateTime - timestamp_LTE: DateTime + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + timestamp: DateTimeScalarFilters + timestamp_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { eq: ... }\\") + timestamp_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { gt: ... }\\") + timestamp_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { gte: ... }\\") + timestamp_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter timestamp: { in: ... }\\") + timestamp_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { lt: ... }\\") + timestamp_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { lte: ... }\\") } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } @@ -843,7 +957,6 @@ describe("Relationship-properties", () => { } type ActorMovieMoviesEdgeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") screenTime: IntAggregateSelection! timestamp: DateTimeAggregateSelection! } @@ -856,7 +969,7 @@ describe("Relationship-properties", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -869,10 +982,6 @@ describe("Relationship-properties", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -882,6 +991,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -919,21 +1047,22 @@ describe("Relationship-properties", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -956,13 +1085,15 @@ describe("Relationship-properties", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -974,45 +1105,47 @@ describe("Relationship-properties", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1047,6 +1180,22 @@ describe("Relationship-properties", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -1055,9 +1204,23 @@ describe("Relationship-properties", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID } type IntAggregateSelection { @@ -1067,10 +1230,35 @@ describe("Relationship-properties", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -1081,7 +1269,6 @@ describe("Relationship-properties", () => { } type MovieActorActorsEdgeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") screenTime: IntAggregateSelection! timestamp: DateTimeAggregateSelection! } @@ -1094,7 +1281,7 @@ describe("Relationship-properties", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1107,10 +1294,6 @@ describe("Relationship-properties", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1120,6 +1303,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -1157,21 +1359,22 @@ describe("Relationship-properties", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1225,13 +1428,15 @@ describe("Relationship-properties", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1243,45 +1448,47 @@ describe("Relationship-properties", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1308,10 +1515,10 @@ describe("Relationship-properties", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1329,6 +1536,27 @@ describe("Relationship-properties", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1391,26 +1619,17 @@ describe("Relationship-properties", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - timestamp_MAX_EQUAL: DateTime - timestamp_MAX_GT: DateTime - timestamp_MAX_GTE: DateTime - timestamp_MAX_LT: DateTime - timestamp_MAX_LTE: DateTime - timestamp_MIN_EQUAL: DateTime - timestamp_MIN_GT: DateTime - timestamp_MIN_GTE: DateTime - timestamp_MIN_LT: DateTime - timestamp_MIN_LTE: DateTime + timestamp: DateTimeScalarAggregationFilters + timestamp_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { eq: ... } } }' instead.\\") + timestamp_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { gt: ... } } }' instead.\\") + timestamp_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { gte: ... } } }' instead.\\") + timestamp_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { lt: ... } } }' instead.\\") + timestamp_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { max: { lte: ... } } }' instead.\\") + timestamp_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { eq: ... } } }' instead.\\") + timestamp_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { gt: ... } } }' instead.\\") + timestamp_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { gte: ... } } }' instead.\\") + timestamp_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { lt: ... } } }' instead.\\") + timestamp_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'timestamp: { min: { lte: ... } } }' instead.\\") } input ActedInSort { @@ -1422,25 +1641,25 @@ describe("Relationship-properties", () => { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - timestamp: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - timestamp_EQ: DateTime - timestamp_GT: DateTime - timestamp_GTE: DateTime - timestamp_IN: [DateTime!] - timestamp_LT: DateTime - timestamp_LTE: DateTime + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + timestamp: DateTimeScalarFilters + timestamp_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { eq: ... }\\") + timestamp_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { gt: ... }\\") + timestamp_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { gte: ... }\\") + timestamp_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter timestamp: { in: ... }\\") + timestamp_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { lt: ... }\\") + timestamp_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter timestamp: { lte: ... }\\") } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } @@ -1482,7 +1701,6 @@ describe("Relationship-properties", () => { } type ActorMovieMoviesEdgeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") timestamp: DateTimeAggregateSelection! } @@ -1494,7 +1712,7 @@ describe("Relationship-properties", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1506,10 +1724,6 @@ describe("Relationship-properties", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1519,6 +1733,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -1555,21 +1788,22 @@ describe("Relationship-properties", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -1591,13 +1825,15 @@ describe("Relationship-properties", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1609,45 +1845,47 @@ describe("Relationship-properties", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1682,6 +1920,22 @@ describe("Relationship-properties", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -1690,15 +1944,39 @@ describe("Relationship-properties", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -1709,7 +1987,6 @@ describe("Relationship-properties", () => { } type MovieActorActorsEdgeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") timestamp: DateTimeAggregateSelection! } @@ -1721,7 +1998,7 @@ describe("Relationship-properties", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1733,10 +2010,6 @@ describe("Relationship-properties", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1746,6 +2019,25 @@ describe("Relationship-properties", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -1782,21 +2074,22 @@ describe("Relationship-properties", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1849,13 +2142,15 @@ describe("Relationship-properties", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1867,45 +2162,47 @@ describe("Relationship-properties", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1932,10 +2229,10 @@ describe("Relationship-properties", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1953,6 +2250,27 @@ describe("Relationship-properties", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/relationship.test.ts b/packages/graphql/tests/schema/directives/relationship.test.ts index 07b502b68a..f3fc4a7a28 100644 --- a/packages/graphql/tests/schema/directives/relationship.test.ts +++ b/packages/graphql/tests/schema/directives/relationship.test.ts @@ -23,373 +23,6 @@ import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; describe("Relationship", () => { - test("Single Relationship", async () => { - const typeDefs = gql` - type Actor @node { - name: String - } - - type Movie @node { - id: ID - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - name: String - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - name: String - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - id: ID - } - - type MovieActorActorsAggregationSelection { - count: Int! - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - } - - input MovieActorsUpdateConnectionInput { - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - id: ID - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(where: ActorWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - test("Multi Relationship", async () => { const typeDefs = gql` type Actor @node { @@ -412,9 +45,9 @@ describe("Relationship", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String } @@ -451,32 +84,22 @@ describe("Relationship", () => { type ActorMovieMoviesAggregationSelection { count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ActorMoviesAggregateInput { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput } input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -486,6 +109,25 @@ describe("Relationship", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -516,22 +158,6 @@ describe("Relationship", () => { create: [ActorMoviesCreateFieldInput!] } - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type ActorMoviesRelationship { cursor: String! node: Movie! @@ -550,13 +176,15 @@ describe("Relationship", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -568,45 +196,47 @@ describe("Relationship", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -641,15 +271,44 @@ describe("Relationship", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -666,7 +325,7 @@ describe("Relationship", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -677,10 +336,6 @@ describe("Relationship", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -690,6 +345,25 @@ describe("Relationship", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -724,21 +398,22 @@ describe("Relationship", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -761,7 +436,6 @@ describe("Relationship", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -790,13 +464,15 @@ describe("Relationship", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -808,45 +484,47 @@ describe("Relationship", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -873,10 +551,10 @@ describe("Relationship", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -894,6 +572,27 @@ describe("Relationship", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/selectable.test.ts b/packages/graphql/tests/schema/directives/selectable.test.ts index 91d6ec5a8f..1caf5630f7 100644 --- a/packages/graphql/tests/schema/directives/selectable.test.ts +++ b/packages/graphql/tests/schema/directives/selectable.test.ts @@ -80,15 +80,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -98,28 +89,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -143,7 +134,7 @@ describe("@selectable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -161,6 +152,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -234,15 +239,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -252,28 +248,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -297,7 +293,7 @@ describe("@selectable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -315,6 +311,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -387,15 +397,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -405,28 +406,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -450,7 +451,7 @@ describe("@selectable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -468,6 +469,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -566,15 +581,6 @@ describe("@selectable", () => { title: String! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -587,25 +593,25 @@ describe("@selectable", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -619,18 +625,18 @@ describe("@selectable", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -654,7 +660,7 @@ describe("@selectable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -672,6 +678,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! @@ -719,7 +739,7 @@ describe("@selectable", () => { } type Actor { - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection name: String! } @@ -727,7 +747,7 @@ describe("@selectable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -737,11 +757,26 @@ describe("@selectable", () => { } input ActorActedInConnectFieldInput { + where: MovieConnectWhere + } + + input ActorActedInConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere } input ActorActedInConnectionWhere { @@ -772,36 +807,38 @@ describe("@selectable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } input ActorActedInUpdateConnectionInput { @@ -846,15 +883,6 @@ describe("@selectable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -864,45 +892,47 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -937,6 +967,26 @@ describe("@selectable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { description: String title: String! @@ -962,13 +1012,15 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -980,28 +1032,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1028,10 +1080,10 @@ describe("@selectable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1049,6 +1101,27 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1093,9 +1166,9 @@ describe("@selectable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -1103,7 +1176,7 @@ describe("@selectable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1113,10 +1186,6 @@ describe("@selectable", () => { } input ActorActedInConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1126,6 +1195,25 @@ describe("@selectable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: MovieSort } @@ -1158,36 +1246,38 @@ describe("@selectable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -1237,15 +1327,6 @@ describe("@selectable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1255,45 +1336,47 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1328,6 +1411,26 @@ describe("@selectable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { description: String title: String! @@ -1353,13 +1456,15 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1371,28 +1476,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1419,10 +1524,10 @@ describe("@selectable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1440,6 +1545,27 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1497,6 +1623,25 @@ describe("@selectable", () => { name: String! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -1618,15 +1763,6 @@ describe("@selectable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1636,44 +1772,46 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: ActorActedInUpdateInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1738,15 +1876,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1756,28 +1885,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1808,30 +1937,35 @@ describe("@selectable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -1863,15 +1997,6 @@ describe("@selectable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -1881,28 +2006,28 @@ describe("@selectable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -1918,6 +2043,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1974,8 +2113,8 @@ describe("@selectable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -1985,6 +2124,25 @@ describe("@selectable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -2111,15 +2269,6 @@ describe("@selectable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -2129,44 +2278,46 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: ActorActedInUpdateInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2231,15 +2382,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2249,28 +2391,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2301,30 +2443,35 @@ describe("@selectable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -2356,15 +2503,6 @@ describe("@selectable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -2374,28 +2512,28 @@ describe("@selectable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -2411,6 +2549,20 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2473,7 +2625,7 @@ describe("@selectable", () => { } type Actor { - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection name: String! } @@ -2481,7 +2633,7 @@ describe("@selectable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2494,6 +2646,25 @@ describe("@selectable", () => { where: ProductionConnectWhere } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { AND: [ActorActedInConnectionWhere!] NOT: ActorActedInConnectionWhere @@ -2522,36 +2693,38 @@ describe("@selectable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } input ActorActedInUpdateConnectionInput { @@ -2586,15 +2759,6 @@ describe("@selectable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -2614,45 +2778,47 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2692,6 +2858,26 @@ describe("@selectable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { description: String title: String! @@ -2713,15 +2899,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2731,28 +2908,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2811,13 +2988,15 @@ describe("@selectable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -2829,30 +3008,29 @@ describe("@selectable", () => { } input ProductionUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -2862,16 +3040,16 @@ describe("@selectable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -2903,15 +3081,6 @@ describe("@selectable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -2921,28 +3090,28 @@ describe("@selectable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -2958,6 +3127,27 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -3017,9 +3207,9 @@ describe("@selectable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -3027,7 +3217,7 @@ describe("@selectable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3046,6 +3236,25 @@ describe("@selectable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: ProductionSort } @@ -3078,36 +3287,38 @@ describe("@selectable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -3147,15 +3358,6 @@ describe("@selectable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -3175,45 +3377,47 @@ describe("@selectable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -3253,6 +3457,26 @@ describe("@selectable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { description: String title: String! @@ -3274,15 +3498,6 @@ describe("@selectable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3292,28 +3507,28 @@ describe("@selectable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -3372,13 +3587,15 @@ describe("@selectable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -3390,30 +3607,29 @@ describe("@selectable", () => { } input ProductionUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -3423,16 +3639,16 @@ describe("@selectable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -3464,15 +3680,6 @@ describe("@selectable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -3482,28 +3689,28 @@ describe("@selectable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -3519,6 +3726,27 @@ describe("@selectable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/settable.test.ts b/packages/graphql/tests/schema/directives/settable.test.ts index be8ef975fd..e50fbcb212 100644 --- a/packages/graphql/tests/schema/directives/settable.test.ts +++ b/packages/graphql/tests/schema/directives/settable.test.ts @@ -80,15 +80,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -98,28 +89,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -143,7 +134,7 @@ describe("@settable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -161,6 +152,20 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -235,15 +240,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -253,26 +249,26 @@ describe("@settable", () => { } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -296,7 +292,7 @@ describe("@settable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -314,6 +310,20 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -413,15 +423,6 @@ describe("@settable", () => { title: String! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -434,23 +435,23 @@ describe("@settable", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -464,18 +465,18 @@ describe("@settable", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -499,7 +500,7 @@ describe("@settable", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -517,6 +518,20 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! @@ -564,9 +579,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -574,7 +589,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -584,10 +599,6 @@ describe("@settable", () => { } input ActorActedInConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -597,6 +608,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: MovieSort } @@ -624,36 +654,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -702,15 +734,6 @@ describe("@settable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -720,45 +743,47 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -793,6 +818,26 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { description: String title: String! @@ -818,13 +863,15 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -836,28 +883,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -884,10 +931,10 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -905,6 +952,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -950,9 +1018,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -960,7 +1028,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -970,10 +1038,6 @@ describe("@settable", () => { } input ActorActedInConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -983,6 +1047,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: MovieSort } @@ -1011,36 +1094,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -1077,15 +1162,6 @@ describe("@settable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -1094,45 +1170,47 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1167,6 +1245,26 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { description: String title: String! @@ -1192,13 +1290,15 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1210,28 +1310,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1258,10 +1358,10 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1279,6 +1379,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1323,9 +1444,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -1333,7 +1454,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1344,10 +1465,6 @@ describe("@settable", () => { input ActorActedInConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1357,6 +1474,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: MovieSort } @@ -1391,36 +1527,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -1469,13 +1607,15 @@ describe("@settable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -1486,45 +1626,47 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1559,10 +1701,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! description: String title: String! } @@ -1580,7 +1742,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1591,10 +1753,6 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1604,6 +1762,25 @@ describe("@settable", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -1638,21 +1815,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1706,13 +1884,15 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1725,53 +1905,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1798,10 +1980,10 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1819,6 +2001,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -1863,9 +2066,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -1873,7 +2076,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1884,10 +2087,6 @@ describe("@settable", () => { input ActorActedInConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1897,6 +2096,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: MovieSort } @@ -1926,36 +2144,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -2016,13 +2236,15 @@ describe("@settable", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -2034,45 +2256,47 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2107,10 +2331,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! description: String title: String! } @@ -2128,7 +2372,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2139,10 +2383,6 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2152,6 +2392,25 @@ describe("@settable", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -2186,21 +2445,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -2254,13 +2514,15 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -2273,53 +2535,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2346,10 +2610,10 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -2367,6 +2631,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2420,8 +2705,8 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -2431,6 +2716,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -2541,15 +2845,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -2559,44 +2854,46 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: ActorActedInUpdateInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2661,15 +2958,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2679,28 +2967,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2731,30 +3019,35 @@ describe("@settable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -2786,15 +3079,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -2804,28 +3088,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -2841,6 +3125,20 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2898,8 +3196,8 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -2909,6 +3207,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -2996,15 +3313,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -3013,44 +3321,46 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -3115,15 +3425,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3133,28 +3434,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -3185,30 +3486,35 @@ describe("@settable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -3240,15 +3546,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -3258,28 +3555,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -3295,6 +3592,20 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -3351,8 +3662,8 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -3367,6 +3678,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -3482,13 +3812,15 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -3499,44 +3831,46 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -3576,10 +3910,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! description: String title: String! } @@ -3597,7 +3951,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3608,10 +3962,6 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3621,6 +3971,25 @@ describe("@settable", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -3655,21 +4024,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -3723,15 +4093,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3742,53 +4103,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -3819,30 +4182,35 @@ describe("@settable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -3874,15 +4242,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -3892,28 +4251,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -3929,6 +4288,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -3985,8 +4365,8 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int, where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -4001,6 +4381,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionWhere { Movie: ActorActedInMovieConnectionWhere Series: ActorActedInSeriesConnectionWhere @@ -4131,13 +4530,15 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -4149,44 +4550,46 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: ActorActedInUpdateInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -4226,10 +4629,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! description: String title: String! } @@ -4247,7 +4670,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4258,10 +4681,6 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -4271,6 +4690,25 @@ describe("@settable", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -4305,21 +4743,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -4373,15 +4812,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -4392,53 +4822,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -4469,30 +4901,35 @@ describe("@settable", () => { union Production = Movie | Series + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + input ProductionWhere { Movie: MovieWhere Series: SeriesWhere } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ProductionWhere): [Production!]! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + productions(limit: Int, offset: Int, where: ProductionWhere): [Production!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - type Series { description: String name: String! @@ -4524,15 +4961,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -4542,28 +4970,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -4579,6 +5007,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -4641,9 +5090,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -4651,7 +5100,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4670,6 +5119,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: ProductionSort } @@ -4697,36 +5165,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -4765,15 +5235,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -4793,45 +5254,47 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -4871,6 +5334,26 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { description: String title: String! @@ -4892,15 +5375,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -4910,28 +5384,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -4990,13 +5464,15 @@ describe("@settable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -5008,30 +5484,29 @@ describe("@settable", () => { } input ProductionUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -5041,16 +5516,16 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -5082,15 +5557,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -5100,28 +5566,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -5137,6 +5603,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -5197,9 +5684,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -5207,7 +5694,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5226,6 +5713,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: ProductionSort } @@ -5254,36 +5760,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -5310,15 +5818,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -5337,45 +5836,47 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -5415,6 +5916,26 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { description: String title: String! @@ -5436,15 +5957,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -5454,28 +5966,28 @@ describe("@settable", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -5534,13 +6046,15 @@ describe("@settable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -5555,20 +6069,19 @@ describe("@settable", () => { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -5578,16 +6091,16 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -5619,15 +6132,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -5637,28 +6141,28 @@ describe("@settable", () => { } input SeriesUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -5674,6 +6178,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -5735,9 +6260,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -5745,7 +6270,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5765,6 +6290,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: ProductionSort } @@ -5799,36 +6343,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -5867,15 +6413,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -5886,6 +6423,17 @@ describe("@settable", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -5894,45 +6442,47 @@ describe("@settable", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -5972,10 +6522,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! } @@ -5993,7 +6563,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6004,11 +6574,26 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -6024,21 +6609,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -6075,15 +6661,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -6094,53 +6671,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -6170,7 +6749,7 @@ describe("@settable", () => { } interface Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! @@ -6180,7 +6759,7 @@ describe("@settable", () => { AND: [ProductionActorsAggregateInput!] NOT: ProductionActorsAggregateInput OR: [ProductionActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6191,10 +6770,6 @@ describe("@settable", () => { input ProductionActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -6204,6 +6779,25 @@ describe("@settable", () => { totalCount: Int! } + input ProductionActorsConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionActorsConnections match this filter + \\"\\"\\" + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere + } + input ProductionActorsConnectionSort { node: ActorSort } @@ -6229,21 +6823,22 @@ describe("@settable", () => { AND: [ProductionActorsNodeAggregationWhereInput!] NOT: ProductionActorsNodeAggregationWhereInput OR: [ProductionActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ProductionActorsRelationship { @@ -6288,13 +6883,15 @@ describe("@settable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -6309,45 +6906,46 @@ describe("@settable", () => { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + actors: ActorRelationshipFilters actorsAggregate: ProductionActorsAggregateInput + actorsConnection: ProductionActorsConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Productions where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Productions where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Productions where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Productions where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -6357,24 +6955,24 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! } @@ -6392,7 +6990,7 @@ describe("@settable", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6403,11 +7001,26 @@ describe("@settable", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -6423,21 +7036,22 @@ describe("@settable", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -6480,15 +7094,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -6499,53 +7104,55 @@ describe("@settable", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -6561,6 +7168,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -6622,9 +7250,9 @@ describe("@settable", () => { } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -6632,7 +7260,7 @@ describe("@settable", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6652,6 +7280,25 @@ describe("@settable", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { node: ProductionSort } @@ -6681,36 +7328,38 @@ describe("@settable", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - description_AVERAGE_LENGTH_EQUAL: Float - description_AVERAGE_LENGTH_GT: Float - description_AVERAGE_LENGTH_GTE: Float - description_AVERAGE_LENGTH_LT: Float - description_AVERAGE_LENGTH_LTE: Float - description_LONGEST_LENGTH_EQUAL: Int - description_LONGEST_LENGTH_GT: Int - description_LONGEST_LENGTH_GTE: Int - description_LONGEST_LENGTH_LT: Int - description_LONGEST_LENGTH_LTE: Int - description_SHORTEST_LENGTH_EQUAL: Int - description_SHORTEST_LENGTH_GT: Int - description_SHORTEST_LENGTH_GTE: Int - description_SHORTEST_LENGTH_LT: Int - description_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -6761,15 +7410,6 @@ describe("@settable", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! node: ActorProductionActedInNodeAggregateSelection @@ -6780,6 +7420,17 @@ describe("@settable", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -6789,45 +7440,47 @@ describe("@settable", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -6867,10 +7520,30 @@ describe("@settable", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! } @@ -6888,7 +7561,7 @@ describe("@settable", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6899,11 +7572,26 @@ describe("@settable", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -6919,21 +7607,22 @@ describe("@settable", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -6970,15 +7659,6 @@ describe("@settable", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -6989,53 +7669,55 @@ describe("@settable", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -7065,7 +7747,7 @@ describe("@settable", () => { } interface Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! @@ -7075,7 +7757,7 @@ describe("@settable", () => { AND: [ProductionActorsAggregateInput!] NOT: ProductionActorsAggregateInput OR: [ProductionActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7086,10 +7768,6 @@ describe("@settable", () => { input ProductionActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -7099,6 +7777,25 @@ describe("@settable", () => { totalCount: Int! } + input ProductionActorsConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionActorsConnections match this filter + \\"\\"\\" + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere + } + input ProductionActorsConnectionSort { node: ActorSort } @@ -7128,21 +7825,22 @@ describe("@settable", () => { AND: [ProductionActorsNodeAggregationWhereInput!] NOT: ProductionActorsNodeAggregationWhereInput OR: [ProductionActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ProductionActorsRelationship { @@ -7200,13 +7898,15 @@ describe("@settable", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -7219,55 +7919,56 @@ describe("@settable", () => { input ProductionUpdateInput { actors: [ProductionActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + actors: ActorRelationshipFilters actorsAggregate: ProductionActorsAggregateInput + actorsConnection: ProductionActorsConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Productions where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Productions where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Productions where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Productions where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -7277,24 +7978,24 @@ describe("@settable", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! description: String title: String! } @@ -7312,7 +8013,7 @@ describe("@settable", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7323,11 +8024,26 @@ describe("@settable", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -7343,21 +8059,22 @@ describe("@settable", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -7400,15 +8117,6 @@ describe("@settable", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -7419,53 +8127,55 @@ describe("@settable", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -7481,6 +8191,27 @@ describe("@settable", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/directives/timestamps.test.ts b/packages/graphql/tests/schema/directives/timestamps.test.ts index 06473882b8..d43cfd0f07 100644 --- a/packages/graphql/tests/schema/directives/timestamps.test.ts +++ b/packages/graphql/tests/schema/directives/timestamps.test.ts @@ -61,6 +61,21 @@ describe("Timestamps", () => { min: DateTime } + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -69,9 +84,18 @@ describe("Timestamps", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -83,7 +107,6 @@ describe("Timestamps", () => { type MovieAggregateSelection { count: Int! createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") updatedAt: DateTimeAggregateSelection! } @@ -97,15 +120,6 @@ describe("Timestamps", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -116,36 +130,36 @@ describe("Timestamps", () => { } input MovieUpdateInput { - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime!] - createdAt_LT: DateTime - createdAt_LTE: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - updatedAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - updatedAt_EQ: DateTime - updatedAt_GT: DateTime - updatedAt_GTE: DateTime - updatedAt_IN: [DateTime!] - updatedAt_LT: DateTime - updatedAt_LTE: DateTime + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + updatedAt: DateTimeScalarFilters + updatedAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { eq: ... }\\") + updatedAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { gt: ... }\\") + updatedAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { gte: ... }\\") + updatedAt_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { in: ... }\\") + updatedAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { lt: ... }\\") + updatedAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { lte: ... }\\") } type MoviesConnection { @@ -169,7 +183,7 @@ describe("Timestamps", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/enum.test.ts b/packages/graphql/tests/schema/enum.test.ts index 347e9928bd..6c64a02db1 100644 --- a/packages/graphql/tests/schema/enum.test.ts +++ b/packages/graphql/tests/schema/enum.test.ts @@ -82,15 +82,6 @@ describe("Enum", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -99,17 +90,17 @@ describe("Enum", () => { } input MovieUpdateInput { - status: Status @deprecated(reason: \\"Please use the explicit _SET field\\") - status_SET: Status + status: StatusEnumScalarMutations + status_SET: Status @deprecated(reason: \\"Please use the generic mutation 'status: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - status: Status @deprecated(reason: \\"Please use the explicit _EQ version\\") - status_EQ: Status - status_IN: [Status] + status: StatusEnumScalarFilters + status_EQ: Status @deprecated(reason: \\"Please use the relevant generic filter status: { eq: ... }\\") + status_IN: [Status] @deprecated(reason: \\"Please use the relevant generic filter status: { in: ... }\\") } type MoviesConnection { @@ -133,7 +124,7 @@ describe("Enum", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -152,6 +143,17 @@ describe("Enum", () => { PENDING } + \\"\\"\\"Status filters\\"\\"\\" + input StatusEnumScalarFilters { + eq: Status + in: [Status!] + } + + \\"\\"\\"Status mutations\\"\\"\\" + input StatusEnumScalarMutations { + set: Status + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/extend.test.ts b/packages/graphql/tests/schema/extend.test.ts index 80f6787d49..f46e54932e 100644 --- a/packages/graphql/tests/schema/extend.test.ts +++ b/packages/graphql/tests/schema/extend.test.ts @@ -63,9 +63,18 @@ describe("Extend", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -75,7 +84,6 @@ describe("Extend", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -89,15 +97,6 @@ describe("Extend", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -107,28 +106,28 @@ describe("Extend", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type MoviesConnection { @@ -152,7 +151,7 @@ describe("Extend", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -170,6 +169,20 @@ describe("Extend", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/federation.test.ts b/packages/graphql/tests/schema/federation.test.ts index cc59bf40f9..b93ccf3d2e 100644 --- a/packages/graphql/tests/schema/federation.test.ts +++ b/packages/graphql/tests/schema/federation.test.ts @@ -34,7 +34,7 @@ describe("Apollo Federation", () => { type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; @@ -95,6 +95,26 @@ describe("Apollo Federation", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! @shareable @@ -113,9 +133,9 @@ describe("Apollo Federation", () => { } type Post { - author: User! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! + author(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + authorAggregate(where: UserWhere): PostUserAuthorAggregationSelection + authorConnection(after: String, first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! content: String! } @@ -128,7 +148,7 @@ describe("Apollo Federation", () => { AND: [PostAuthorAggregateInput!] NOT: PostAuthorAggregateInput OR: [PostAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -138,11 +158,7 @@ describe("Apollo Federation", () => { } input PostAuthorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [UserConnectInput!] where: UserConnectWhere } @@ -152,6 +168,25 @@ describe("Apollo Federation", () => { totalCount: Int! } + input PostAuthorConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + all: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + none: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + single: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + some: PostAuthorConnectionWhere + } + input PostAuthorConnectionSort { node: UserSort } @@ -178,29 +213,30 @@ describe("Apollo Federation", () => { } input PostAuthorFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] } input PostAuthorNodeAggregationWhereInput { AND: [PostAuthorNodeAggregationWhereInput!] NOT: PostAuthorNodeAggregationWhereInput OR: [PostAuthorNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type PostAuthorRelationship { @@ -213,16 +249,16 @@ describe("Apollo Federation", () => { } input PostAuthorUpdateFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput - delete: PostAuthorDeleteFieldInput - disconnect: PostAuthorDisconnectFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] + delete: [PostAuthorDeleteFieldInput!] + disconnect: [PostAuthorDisconnectFieldInput!] update: PostAuthorUpdateConnectionInput where: PostAuthorConnectionWhere } input PostConnectInput { - author: PostAuthorConnectFieldInput + author: [PostAuthorConnectFieldInput!] } input PostConnectWhere { @@ -235,11 +271,11 @@ describe("Apollo Federation", () => { } input PostDeleteInput { - author: PostAuthorDeleteFieldInput + author: [PostAuthorDeleteFieldInput!] } input PostDisconnectInput { - author: PostAuthorDisconnectFieldInput + author: [PostAuthorDisconnectFieldInput!] } type PostEdge { @@ -247,13 +283,15 @@ describe("Apollo Federation", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] + input PostRelationshipFilters { + \\"\\"\\"Filter type where all of the related Posts match this filter\\"\\"\\" + all: PostWhere + \\"\\"\\"Filter type where none of the related Posts match this filter\\"\\"\\" + none: PostWhere + \\"\\"\\"Filter type where one of the related Posts match this filter\\"\\"\\" + single: PostWhere + \\"\\"\\"Filter type where some of the related Posts match this filter\\"\\"\\" + some: PostWhere } \\"\\"\\" @@ -264,9 +302,9 @@ describe("Apollo Federation", () => { } input PostUpdateInput { - author: PostAuthorUpdateFieldInput - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String + author: [PostAuthorUpdateFieldInput!] + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") } type PostUserAuthorAggregationSelection { @@ -282,15 +320,39 @@ describe("Apollo Federation", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] - author: UserWhere + author: UserRelationshipFilters authorAggregate: PostAuthorAggregateInput - authorConnection: PostAuthorConnectionWhere - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String!] - content_STARTS_WITH: String + authorConnection: PostAuthorConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_ALL: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_NONE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SINGLE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SOME: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + author_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + author_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + author_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + author_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { some: ... }' instead.\\") + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") } type PostsConnection { @@ -301,10 +363,10 @@ describe("Apollo Federation", () => { type Query { _service: _Service! - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! @shareable + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! @shareable usersAggregate(where: UserWhere): UserAggregateSelection! @shareable usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! @shareable } @@ -322,6 +384,27 @@ describe("Apollo Federation", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -344,9 +427,9 @@ describe("Apollo Federation", () => { type User @shareable { name: String! - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): UserPostPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postsAggregate(where: PostWhere): UserPostPostsAggregationSelection + postsConnection(after: String, first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! } type UserAggregateSelection @shareable { @@ -380,15 +463,6 @@ describe("Apollo Federation", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - type UserPostPostsAggregationSelection { count: Int! node: UserPostPostsNodeAggregateSelection @@ -402,7 +476,7 @@ describe("Apollo Federation", () => { AND: [UserPostsAggregateInput!] NOT: UserPostsAggregateInput OR: [UserPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -413,10 +487,6 @@ describe("Apollo Federation", () => { input UserPostsConnectFieldInput { connect: [PostConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PostConnectWhere } @@ -426,6 +496,25 @@ describe("Apollo Federation", () => { totalCount: Int! } + input UserPostsConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserPostsConnections match this filter + \\"\\"\\" + all: UserPostsConnectionWhere + \\"\\"\\" + Return Users where none of the related UserPostsConnections match this filter + \\"\\"\\" + none: UserPostsConnectionWhere + \\"\\"\\" + Return Users where one of the related UserPostsConnections match this filter + \\"\\"\\" + single: UserPostsConnectionWhere + \\"\\"\\" + Return Users where some of the related UserPostsConnections match this filter + \\"\\"\\" + some: UserPostsConnectionWhere + } + input UserPostsConnectionSort { node: PostSort } @@ -460,21 +549,22 @@ describe("Apollo Federation", () => { AND: [UserPostsNodeAggregationWhereInput!] NOT: UserPostsNodeAggregationWhereInput OR: [UserPostsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int + content: StringScalarAggregationFilters + content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { eq: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gte: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lte: ... } } }' instead.\\") + content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { eq: ... } } }' instead.\\") + content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gt: ... } } }' instead.\\") + content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gte: ... } } }' instead.\\") + content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lt: ... } } }' instead.\\") + content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { eq: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lte: ... } } }' instead.\\") } type UserPostsRelationship { @@ -495,6 +585,17 @@ describe("Apollo Federation", () => { where: UserPostsConnectionWhere } + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere + } + \\"\\"\\" Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. \\"\\"\\" @@ -503,8 +604,8 @@ describe("Apollo Federation", () => { } input UserUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") posts: [UserPostsUpdateFieldInput!] } @@ -512,37 +613,39 @@ describe("Apollo Federation", () => { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + posts: PostRelationshipFilters postsAggregate: UserPostsAggregateInput + postsConnection: UserPostsConnectionFilters \\"\\"\\" Return Users where all of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_ALL: UserPostsConnectionWhere + postsConnection_ALL: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_NONE: UserPostsConnectionWhere + postsConnection_NONE: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_SINGLE: UserPostsConnectionWhere + postsConnection_SINGLE: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserPostsConnections match this filter \\"\\"\\" - postsConnection_SOME: UserPostsConnectionWhere + postsConnection_SOME: UserPostsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Posts match this filter\\"\\"\\" - posts_ALL: PostWhere + posts_ALL: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Posts match this filter\\"\\"\\" - posts_NONE: PostWhere + posts_NONE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Posts match this filter\\"\\"\\" - posts_SINGLE: PostWhere + posts_SINGLE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Posts match this filter\\"\\"\\" - posts_SOME: PostWhere + posts_SOME: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'posts: { some: ... }' instead.\\") } type UsersConnection @shareable { @@ -584,7 +687,7 @@ describe("Apollo Federation", () => { type Post @node { content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) + author: [User!]! @relationship(type: "HAS_AUTHOR", direction: OUT) } `; @@ -645,6 +748,26 @@ describe("Apollo Federation", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! @@ -663,9 +786,9 @@ describe("Apollo Federation", () => { } type Post { - author: User! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! + author(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + authorAggregate(where: UserWhere): PostUserAuthorAggregationSelection + authorConnection(after: String, first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! content: String! } @@ -678,7 +801,7 @@ describe("Apollo Federation", () => { AND: [PostAuthorAggregateInput!] NOT: PostAuthorAggregateInput OR: [PostAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -688,10 +811,6 @@ describe("Apollo Federation", () => { } input PostAuthorConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: UserConnectWhere } @@ -701,6 +820,25 @@ describe("Apollo Federation", () => { totalCount: Int! } + input PostAuthorConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + all: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + none: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + single: PostAuthorConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + some: PostAuthorConnectionWhere + } + input PostAuthorConnectionSort { node: UserSort } @@ -725,29 +863,30 @@ describe("Apollo Federation", () => { } input PostAuthorFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] } input PostAuthorNodeAggregationWhereInput { AND: [PostAuthorNodeAggregationWhereInput!] NOT: PostAuthorNodeAggregationWhereInput OR: [PostAuthorNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type PostAuthorRelationship { @@ -760,10 +899,10 @@ describe("Apollo Federation", () => { } input PostAuthorUpdateFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput - delete: PostAuthorDeleteFieldInput - disconnect: PostAuthorDisconnectFieldInput + connect: [PostAuthorConnectFieldInput!] + create: [PostAuthorCreateFieldInput!] + delete: [PostAuthorDeleteFieldInput!] + disconnect: [PostAuthorDisconnectFieldInput!] update: PostAuthorUpdateConnectionInput where: PostAuthorConnectionWhere } @@ -774,7 +913,7 @@ describe("Apollo Federation", () => { } input PostDeleteInput { - author: PostAuthorDeleteFieldInput + author: [PostAuthorDeleteFieldInput!] } type PostEdge { @@ -782,15 +921,6 @@ describe("Apollo Federation", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - \\"\\"\\" Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. \\"\\"\\" @@ -799,9 +929,9 @@ describe("Apollo Federation", () => { } input PostUpdateInput { - author: PostAuthorUpdateFieldInput - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String + author: [PostAuthorUpdateFieldInput!] + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") } type PostUserAuthorAggregationSelection { @@ -817,15 +947,39 @@ describe("Apollo Federation", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] - author: UserWhere + author: UserRelationshipFilters authorAggregate: PostAuthorAggregateInput - authorConnection: PostAuthorConnectionWhere - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String!] - content_STARTS_WITH: String + authorConnection: PostAuthorConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_ALL: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_NONE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SINGLE: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostAuthorConnections match this filter + \\"\\"\\" + authorConnection_SOME: PostAuthorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'authorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + author_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + author_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + author_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + author_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'author: { some: ... }' instead.\\") + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") } type PostsConnection { @@ -837,10 +991,10 @@ describe("Apollo Federation", () => { type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -858,6 +1012,27 @@ describe("Apollo Federation", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -900,13 +1075,15 @@ describe("Apollo Federation", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere } \\"\\"\\" @@ -917,20 +1094,20 @@ describe("Apollo Federation", () => { } input UserUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/fulltext.test.ts b/packages/graphql/tests/schema/fulltext.test.ts index 0f6bd99d1e..2338f489b9 100644 --- a/packages/graphql/tests/schema/fulltext.test.ts +++ b/packages/graphql/tests/schema/fulltext.test.ts @@ -18,8 +18,8 @@ */ import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { lexicographicSortSchema } from "graphql/utilities"; import { gql } from "graphql-tag"; +import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../src"; describe("@fulltext schema", () => { @@ -29,8 +29,8 @@ describe("@fulltext schema", () => { @node @fulltext( indexes: [ - { name: "MovieTitle", fields: ["title"] } - { name: "MovieDescription", fields: ["description"] } + { indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] } + { indexName: "MovieDescription", queryName: "moviesByDescription", fields: ["description"] } ] ) { title: String @@ -95,46 +95,24 @@ describe("@fulltext schema", () => { node: Movie! } - input MovieFulltext { - MovieDescription: MovieMovieDescriptionFulltext - MovieTitle: MovieMovieTitleFulltext - } - - \\"\\"\\"The result of a fulltext search on an index of Movie\\"\\"\\" - type MovieFulltextResult { - movie: Movie! + type MovieIndexEdge { + cursor: String! + node: Movie! score: Float! } - \\"\\"\\"The input for sorting a fulltext query on an index of Movie\\"\\"\\" - input MovieFulltextSort { - movie: MovieSort + \\"\\"\\"The input for sorting a Fulltext query on an index of Movie\\"\\"\\" + input MovieIndexSort { + node: MovieSort score: SortDirection } - \\"\\"\\"The input for filtering a fulltext query on an index of Movie\\"\\"\\" - input MovieFulltextWhere { - movie: MovieWhere + \\"\\"\\"The input for filtering a full-text query on an index of Movie\\"\\"\\" + input MovieIndexWhere { + node: MovieWhere score: FloatWhere } - input MovieMovieDescriptionFulltext { - phrase: String! - } - - input MovieMovieTitleFulltext { - phrase: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -144,28 +122,28 @@ describe("@fulltext schema", () => { } input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -174,6 +152,12 @@ describe("@fulltext schema", () => { totalCount: Int! } + type MoviesIndexConnection { + edges: [MovieIndexEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + type Mutation { createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! deleteMovies(where: MovieWhere): DeleteInfo! @@ -189,42 +173,11 @@ describe("@fulltext schema", () => { } type Query { - movies( - \\"\\"\\" - Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score. - \\"\\"\\" - fulltext: MovieFulltext - limit: Int - offset: Int - options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\") - sort: [MovieSort!] - where: MovieWhere - ): [Movie!]! - moviesAggregate( - \\"\\"\\" - Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score. - \\"\\"\\" - fulltext: MovieFulltext - where: MovieWhere - ): MovieAggregateSelection! - moviesConnection( - after: String - first: Int - \\"\\"\\" - Query a full-text index. Allows for the aggregation of results, but does not return the query score. Use the root full-text query fields if you require the score. - \\"\\"\\" - fulltext: MovieFulltext - sort: [MovieSort!] - where: MovieWhere - ): MoviesConnection! - \\"\\"\\" - Query a full-text index. This query returns the query score, but does not allow for aggregations. Use the \`fulltext\` argument under other queries for this functionality. - \\"\\"\\" - moviesFulltextMovieDescription(limit: Int, offset: Int, phrase: String!, sort: [MovieFulltextSort!], where: MovieFulltextWhere): [MovieFulltextResult!]! - \\"\\"\\" - Query a full-text index. This query returns the query score, but does not allow for aggregations. Use the \`fulltext\` argument under other queries for this functionality. - \\"\\"\\" - moviesFulltextMovieTitle(limit: Int, offset: Int, phrase: String!, sort: [MovieFulltextSort!], where: MovieFulltextWhere): [MovieFulltextResult!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesByDescription(after: String, first: Int, phrase: String!, sort: [MovieIndexSort!], where: MovieIndexWhere): MoviesIndexConnection! + moviesByTitle(after: String, first: Int, phrase: String!, sort: [MovieIndexSort!], where: MovieIndexWhere): MoviesIndexConnection! + moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -240,6 +193,20 @@ describe("@fulltext schema", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/global-node.test.ts b/packages/graphql/tests/schema/global-node.test.ts index 264af12eaa..6617cacd88 100644 --- a/packages/graphql/tests/schema/global-node.test.ts +++ b/packages/graphql/tests/schema/global-node.test.ts @@ -60,9 +60,18 @@ describe("Node Interface Types", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie implements Node { @@ -73,7 +82,6 @@ describe("Node Interface Types", () => { type MovieAggregateSelection { count: Int! - imdb: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -87,15 +95,6 @@ describe("Node Interface Types", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -105,10 +104,10 @@ describe("Node Interface Types", () => { } input MovieUpdateInput { - imdb: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - imdb_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + imdb: IDScalarMutations + imdb_SET: ID @deprecated(reason: \\"Please use the generic mutation 'imdb: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { @@ -116,18 +115,18 @@ describe("Node Interface Types", () => { NOT: MovieWhere OR: [MovieWhere!] id: ID - imdb: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdb_CONTAINS: ID - imdb_ENDS_WITH: ID - imdb_EQ: ID - imdb_IN: [ID!] - imdb_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + imdb: IDScalarFilters + imdb_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter imdb: { contains: ... }\\") + imdb_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter imdb: { endsWith: ... }\\") + imdb_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter imdb: { eq: ... }\\") + imdb_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter imdb: { in: ... }\\") + imdb_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter imdb: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -157,7 +156,7 @@ describe("Node Interface Types", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! \\"\\"\\"Fetches an object given its ID\\"\\"\\" @@ -180,6 +179,20 @@ describe("Node Interface Types", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/inheritance.test.ts b/packages/graphql/tests/schema/inheritance.test.ts index 74057cb385..d4665a351f 100644 --- a/packages/graphql/tests/schema/inheritance.test.ts +++ b/packages/graphql/tests/schema/inheritance.test.ts @@ -59,9 +59,9 @@ describe("inheritance", () => { directive @customDirectiveObj on OBJECT type Actor implements Person @customDirectiveObj { - friends(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - friendsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): ActorPersonFriendsAggregationSelection - friendsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonFriendsConnectionSort!], where: PersonFriendsConnectionWhere): PersonFriendsConnection! + friends(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + friendsAggregate(where: PersonWhere): ActorPersonFriendsAggregationSelection + friendsConnection(after: String, first: Int, sort: [PersonFriendsConnectionSort!], where: PersonFriendsConnectionWhere): PersonFriendsConnection! name: String } @@ -88,7 +88,7 @@ describe("inheritance", () => { AND: [ActorFriendsAggregateInput!] NOT: ActorFriendsAggregateInput OR: [ActorFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -104,6 +104,25 @@ describe("inheritance", () => { where: PersonConnectWhere } + input ActorFriendsConnectionFilters { + \\"\\"\\" + Return Actors where all of the related PersonFriendsConnections match this filter + \\"\\"\\" + all: PersonFriendsConnectionWhere + \\"\\"\\" + Return Actors where none of the related PersonFriendsConnections match this filter + \\"\\"\\" + none: PersonFriendsConnectionWhere + \\"\\"\\" + Return Actors where one of the related PersonFriendsConnections match this filter + \\"\\"\\" + single: PersonFriendsConnectionWhere + \\"\\"\\" + Return Actors where some of the related PersonFriendsConnections match this filter + \\"\\"\\" + some: PersonFriendsConnectionWhere + } + input ActorFriendsCreateFieldInput { edge: FriendsWithCreateInput node: PersonCreateInput! @@ -128,21 +147,22 @@ describe("inheritance", () => { AND: [ActorFriendsNodeAggregationWhereInput!] NOT: ActorFriendsNodeAggregationWhereInput OR: [ActorFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input ActorFriendsUpdateConnectionInput { @@ -159,15 +179,6 @@ describe("inheritance", () => { where: PersonFriendsConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorPersonFriendsAggregationSelection { count: Int! edge: ActorPersonFriendsEdgeAggregateSelection @@ -191,45 +202,47 @@ describe("inheritance", () => { input ActorUpdateInput { friends: [ActorFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + friends: PersonRelationshipFilters friendsAggregate: ActorFriendsAggregateInput + friendsConnection: ActorFriendsConnectionFilters \\"\\"\\" Return Actors where all of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_ALL: PersonFriendsConnectionWhere + friendsConnection_ALL: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_NONE: PersonFriendsConnectionWhere + friendsConnection_NONE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SINGLE: PersonFriendsConnectionWhere + friendsConnection_SINGLE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SOME: PersonFriendsConnectionWhere + friendsConnection_SOME: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related People match this filter\\"\\"\\" - friends_ALL: PersonWhere + friends_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related People match this filter\\"\\"\\" - friends_NONE: PersonWhere + friends_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related People match this filter\\"\\"\\" - friends_SINGLE: PersonWhere + friends_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related People match this filter\\"\\"\\" - friends_SOME: PersonWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + friends_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -259,6 +272,16 @@ describe("inheritance", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + \\"\\"\\" The edge properties for the following fields: * Actor.friends @@ -271,26 +294,27 @@ describe("inheritance", () => { AND: [FriendsWithAggregationWhereInput!] NOT: FriendsWithAggregationWhereInput OR: [FriendsWithAggregationWhereInput!] - since_AVERAGE_EQUAL: Float - since_AVERAGE_GT: Float - since_AVERAGE_GTE: Float - since_AVERAGE_LT: Float - since_AVERAGE_LTE: Float - since_MAX_EQUAL: Int - since_MAX_GT: Int - since_MAX_GTE: Int - since_MAX_LT: Int - since_MAX_LTE: Int - since_MIN_EQUAL: Int - since_MIN_GT: Int - since_MIN_GTE: Int - since_MIN_LT: Int - since_MIN_LTE: Int - since_SUM_EQUAL: Int - since_SUM_GT: Int - since_SUM_GTE: Int - since_SUM_LT: Int - since_SUM_LTE: Int + since: IntScalarAggregationFilters + since_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'since: { average: { eq: ... } } }' instead.\\") + since_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'since: { average: { gt: ... } } }' instead.\\") + since_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'since: { average: { gte: ... } } }' instead.\\") + since_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'since: { average: { lt: ... } } }' instead.\\") + since_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'since: { average: { lte: ... } } }' instead.\\") + since_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { eq: ... } } }' instead.\\") + since_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { gt: ... } } }' instead.\\") + since_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { gte: ... } } }' instead.\\") + since_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { lt: ... } } }' instead.\\") + since_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { lte: ... } } }' instead.\\") + since_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { eq: ... } } }' instead.\\") + since_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { gt: ... } } }' instead.\\") + since_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { gte: ... } } }' instead.\\") + since_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { lt: ... } } }' instead.\\") + since_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { lte: ... } } }' instead.\\") + since_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { sum: { eq: ... } } }' instead.\\") + since_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { sum: { gt: ... } } }' instead.\\") + since_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { sum: { gte: ... } } }' instead.\\") + since_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { sum: { lt: ... } } }' instead.\\") + since_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'since: { sum: { lte: ... } } }' instead.\\") } input FriendsWithCreateInput { @@ -302,23 +326,23 @@ describe("inheritance", () => { } input FriendsWithUpdateInput { - since: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - since_DECREMENT: Int - since_INCREMENT: Int - since_SET: Int + since: IntScalarMutations + since_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'since: { decrement: ... } }' instead.\\") + since_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'since: { increment: ... } }' instead.\\") + since_SET: Int @deprecated(reason: \\"Please use the generic mutation 'since: { set: ... } }' instead.\\") } input FriendsWithWhere { AND: [FriendsWithWhere!] NOT: FriendsWithWhere OR: [FriendsWithWhere!] - since: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - since_EQ: Int - since_GT: Int - since_GTE: Int - since_IN: [Int] - since_LT: Int - since_LTE: Int + since: IntScalarFilters + since_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter since: { eq: ... }\\") + since_GT: Int @deprecated(reason: \\"Please use the relevant generic filter since: { gt: ... }\\") + since_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter since: { gte: ... }\\") + since_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter since: { in: ... }\\") + since_LT: Int @deprecated(reason: \\"Please use the relevant generic filter since: { lt: ... }\\") + since_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter since: { lte: ... }\\") } type IntAggregateSelection { @@ -328,6 +352,31 @@ describe("inheritance", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Mutation { createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! @@ -349,7 +398,7 @@ describe("inheritance", () => { } interface Person @customDirectiveInter { - friends(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! @customDirectiveField + friends(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! @customDirectiveField friendsConnection(after: String, first: Int, sort: [PersonFriendsConnectionSort!], where: PersonFriendsConnectionWhere): PersonFriendsConnection! name: String @customDirectiveField } @@ -388,7 +437,7 @@ describe("inheritance", () => { AND: [PersonFriendsAggregateInput!] NOT: PersonFriendsAggregateInput OR: [PersonFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -410,6 +459,25 @@ describe("inheritance", () => { totalCount: Int! } + input PersonFriendsConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonFriendsConnections match this filter + \\"\\"\\" + all: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where none of the related PersonFriendsConnections match this filter + \\"\\"\\" + none: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where one of the related PersonFriendsConnections match this filter + \\"\\"\\" + single: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where some of the related PersonFriendsConnections match this filter + \\"\\"\\" + some: PersonFriendsConnectionWhere + } + input PersonFriendsConnectionSort { edge: PersonFriendsEdgeSort node: PersonSort @@ -482,21 +550,22 @@ describe("inheritance", () => { AND: [PersonFriendsNodeAggregationWhereInput!] NOT: PersonFriendsNodeAggregationWhereInput OR: [PersonFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type PersonFriendsRelationship { @@ -525,13 +594,15 @@ describe("inheritance", () => { Actor } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -543,7 +614,7 @@ describe("inheritance", () => { input PersonUpdateInput { friends: [PersonFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") + name: StringScalarMutations name_SET: String } @@ -551,46 +622,47 @@ describe("inheritance", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] + friends: PersonRelationshipFilters friendsAggregate: PersonFriendsAggregateInput + friendsConnection: PersonFriendsConnectionFilters \\"\\"\\" Return People where all of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_ALL: PersonFriendsConnectionWhere + friendsConnection_ALL: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_NONE: PersonFriendsConnectionWhere + friendsConnection_NONE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SINGLE: PersonFriendsConnectionWhere + friendsConnection_SINGLE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SOME: PersonFriendsConnectionWhere + friendsConnection_SOME: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related People match this filter\\"\\"\\" - friends_ALL: PersonWhere + friends_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related People match this filter\\"\\"\\" - friends_NONE: PersonWhere + friends_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related People match this filter\\"\\"\\" - friends_SINGLE: PersonWhere + friends_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related People match this filter\\"\\"\\" - friends_SOME: PersonWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + friends_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [PersonImplementation!] - typename_IN: [PersonImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -608,6 +680,27 @@ describe("inheritance", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/inputs.test.ts b/packages/graphql/tests/schema/inputs.test.ts index 3d21e8ac0b..aed9b70780 100644 --- a/packages/graphql/tests/schema/inputs.test.ts +++ b/packages/graphql/tests/schema/inputs.test.ts @@ -67,9 +67,18 @@ describe("Inputs", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -78,7 +87,6 @@ describe("Inputs", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -90,15 +98,6 @@ describe("Inputs", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -107,20 +106,20 @@ describe("Inputs", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -148,7 +147,7 @@ describe("Inputs", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! name(input: NodeInput): String diff --git a/packages/graphql/tests/schema/interface-relationships.test.ts b/packages/graphql/tests/schema/interface-relationships.test.ts index 01187dc39c..8657de6b3e 100644 --- a/packages/graphql/tests/schema/interface-relationships.test.ts +++ b/packages/graphql/tests/schema/interface-relationships.test.ts @@ -69,26 +69,27 @@ describe("Interface Relationships", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -100,29 +101,29 @@ describe("Interface Relationships", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -130,7 +131,7 @@ describe("Interface Relationships", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -151,6 +152,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -186,21 +206,22 @@ describe("Interface Relationships", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -242,15 +263,6 @@ describe("Interface Relationships", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -274,45 +286,47 @@ describe("Interface Relationships", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -352,6 +366,16 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -359,6 +383,31 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { runtime: Int! title: String! @@ -380,15 +429,6 @@ describe("Interface Relationships", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -398,31 +438,31 @@ describe("Interface Relationships", () => { } input MovieUpdateInput { - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -479,13 +519,15 @@ describe("Interface Relationships", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -496,22 +538,21 @@ describe("Interface Relationships", () => { } input ProductionUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -521,16 +562,16 @@ describe("Interface Relationships", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } @@ -562,15 +603,6 @@ describe("Interface Relationships", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -580,31 +612,31 @@ describe("Interface Relationships", () => { } input SeriesUpdateInput { - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -620,6 +652,27 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -651,7 +704,7 @@ describe("Interface Relationships", () => { const typeDefs = gql` type Episode @node { runtime: Int! - series: Series! @relationship(type: "HAS_EPISODE", direction: IN) + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -704,26 +757,27 @@ describe("Interface Relationships", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -735,29 +789,29 @@ describe("Interface Relationships", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -765,7 +819,7 @@ describe("Interface Relationships", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -787,6 +841,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -824,21 +897,22 @@ describe("Interface Relationships", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -892,15 +966,6 @@ describe("Interface Relationships", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -915,6 +980,17 @@ describe("Interface Relationships", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -924,45 +1000,47 @@ describe("Interface Relationships", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -1009,9 +1087,9 @@ describe("Interface Relationships", () => { type Episode { runtime: Int! - series(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): Series! - seriesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: SeriesWhere): EpisodeSeriesSeriesAggregationSelection - seriesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [EpisodeSeriesConnectionSort!], where: EpisodeSeriesConnectionWhere): EpisodeSeriesConnection! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): EpisodeSeriesSeriesAggregationSelection + seriesConnection(after: String, first: Int, sort: [EpisodeSeriesConnectionSort!], where: EpisodeSeriesConnectionWhere): EpisodeSeriesConnection! } type EpisodeAggregateSelection { @@ -1020,7 +1098,7 @@ describe("Interface Relationships", () => { } input EpisodeConnectInput { - series: EpisodeSeriesConnectFieldInput + series: [EpisodeSeriesConnectFieldInput!] } input EpisodeConnectWhere { @@ -1033,11 +1111,11 @@ describe("Interface Relationships", () => { } input EpisodeDeleteInput { - series: EpisodeSeriesDeleteFieldInput + series: [EpisodeSeriesDeleteFieldInput!] } input EpisodeDisconnectInput { - series: EpisodeSeriesDisconnectFieldInput + series: [EpisodeSeriesDisconnectFieldInput!] } type EpisodeEdge { @@ -1045,20 +1123,22 @@ describe("Interface Relationships", () => { node: Episode! } - input EpisodeOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more EpisodeSort objects to sort Episodes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [EpisodeSort!] + input EpisodeRelationshipFilters { + \\"\\"\\"Filter type where all of the related Episodes match this filter\\"\\"\\" + all: EpisodeWhere + \\"\\"\\"Filter type where none of the related Episodes match this filter\\"\\"\\" + none: EpisodeWhere + \\"\\"\\"Filter type where one of the related Episodes match this filter\\"\\"\\" + single: EpisodeWhere + \\"\\"\\"Filter type where some of the related Episodes match this filter\\"\\"\\" + some: EpisodeWhere } input EpisodeSeriesAggregateInput { AND: [EpisodeSeriesAggregateInput!] NOT: EpisodeSeriesAggregateInput OR: [EpisodeSeriesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1068,11 +1148,7 @@ describe("Interface Relationships", () => { } input EpisodeSeriesConnectFieldInput { - connect: SeriesConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [SeriesConnectInput!] where: SeriesConnectWhere } @@ -1082,6 +1158,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input EpisodeSeriesConnectionFilters { + \\"\\"\\" + Return Episodes where all of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + all: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where none of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + none: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where one of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + single: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where some of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + some: EpisodeSeriesConnectionWhere + } + input EpisodeSeriesConnectionSort { node: SeriesSort } @@ -1108,49 +1203,51 @@ describe("Interface Relationships", () => { } input EpisodeSeriesFieldInput { - connect: EpisodeSeriesConnectFieldInput - create: EpisodeSeriesCreateFieldInput + connect: [EpisodeSeriesConnectFieldInput!] + create: [EpisodeSeriesCreateFieldInput!] } input EpisodeSeriesNodeAggregationWhereInput { AND: [EpisodeSeriesNodeAggregationWhereInput!] NOT: EpisodeSeriesNodeAggregationWhereInput OR: [EpisodeSeriesNodeAggregationWhereInput!] - episodeCount_AVERAGE_EQUAL: Float - episodeCount_AVERAGE_GT: Float - episodeCount_AVERAGE_GTE: Float - episodeCount_AVERAGE_LT: Float - episodeCount_AVERAGE_LTE: Float - episodeCount_MAX_EQUAL: Int - episodeCount_MAX_GT: Int - episodeCount_MAX_GTE: Int - episodeCount_MAX_LT: Int - episodeCount_MAX_LTE: Int - episodeCount_MIN_EQUAL: Int - episodeCount_MIN_GT: Int - episodeCount_MIN_GTE: Int - episodeCount_MIN_LT: Int - episodeCount_MIN_LTE: Int - episodeCount_SUM_EQUAL: Int - episodeCount_SUM_GT: Int - episodeCount_SUM_GTE: Int - episodeCount_SUM_LT: Int - episodeCount_SUM_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + episodeCount: IntScalarAggregationFilters + episodeCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { eq: ... } } }' instead.\\") + episodeCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { gt: ... } } }' instead.\\") + episodeCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { gte: ... } } }' instead.\\") + episodeCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { lt: ... } } }' instead.\\") + episodeCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { lte: ... } } }' instead.\\") + episodeCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { eq: ... } } }' instead.\\") + episodeCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { gt: ... } } }' instead.\\") + episodeCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { gte: ... } } }' instead.\\") + episodeCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { lt: ... } } }' instead.\\") + episodeCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { lte: ... } } }' instead.\\") + episodeCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { eq: ... } } }' instead.\\") + episodeCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { gt: ... } } }' instead.\\") + episodeCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { gte: ... } } }' instead.\\") + episodeCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { lt: ... } } }' instead.\\") + episodeCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { lte: ... } } }' instead.\\") + episodeCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { eq: ... } } }' instead.\\") + episodeCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { gt: ... } } }' instead.\\") + episodeCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { gte: ... } } }' instead.\\") + episodeCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { lt: ... } } }' instead.\\") + episodeCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type EpisodeSeriesRelationship { @@ -1173,10 +1270,10 @@ describe("Interface Relationships", () => { } input EpisodeSeriesUpdateFieldInput { - connect: EpisodeSeriesConnectFieldInput - create: EpisodeSeriesCreateFieldInput - delete: EpisodeSeriesDeleteFieldInput - disconnect: EpisodeSeriesDisconnectFieldInput + connect: [EpisodeSeriesConnectFieldInput!] + create: [EpisodeSeriesCreateFieldInput!] + delete: [EpisodeSeriesDeleteFieldInput!] + disconnect: [EpisodeSeriesDisconnectFieldInput!] update: EpisodeSeriesUpdateConnectionInput where: EpisodeSeriesConnectionWhere } @@ -1189,27 +1286,51 @@ describe("Interface Relationships", () => { } input EpisodeUpdateInput { - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - series: EpisodeSeriesUpdateFieldInput + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + series: [EpisodeSeriesUpdateFieldInput!] } input EpisodeWhere { AND: [EpisodeWhere!] NOT: EpisodeWhere OR: [EpisodeWhere!] - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - series: SeriesWhere + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + series: SeriesRelationshipFilters seriesAggregate: EpisodeSeriesAggregateInput - seriesConnection: EpisodeSeriesConnectionWhere + seriesConnection: EpisodeSeriesConnectionFilters + \\"\\"\\" + Return Episodes where all of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_ALL: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where none of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_NONE: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where one of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_SINGLE: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where some of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_SOME: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Episodes where all of the related Series match this filter\\"\\"\\" + series_ALL: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { all: ... }' instead.\\") + \\"\\"\\"Return Episodes where none of the related Series match this filter\\"\\"\\" + series_NONE: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { none: ... }' instead.\\") + \\"\\"\\"Return Episodes where one of the related Series match this filter\\"\\"\\" + series_SINGLE: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { single: ... }' instead.\\") + \\"\\"\\"Return Episodes where some of the related Series match this filter\\"\\"\\" + series_SOME: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { some: ... }' instead.\\") } type EpisodesConnection { @@ -1218,6 +1339,16 @@ describe("Interface Relationships", () => { totalCount: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -1225,10 +1356,35 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! runtime: Int! title: String! } @@ -1251,7 +1407,7 @@ describe("Interface Relationships", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1264,11 +1420,26 @@ describe("Interface Relationships", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -1285,21 +1456,22 @@ describe("Interface Relationships", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -1337,15 +1509,6 @@ describe("Interface Relationships", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1356,56 +1519,58 @@ describe("Interface Relationships", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1438,7 +1603,7 @@ describe("Interface Relationships", () => { } interface Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! title: String! } @@ -1447,7 +1612,7 @@ describe("Interface Relationships", () => { AND: [ProductionActorsAggregateInput!] NOT: ProductionActorsAggregateInput OR: [ProductionActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1460,10 +1625,6 @@ describe("Interface Relationships", () => { input ProductionActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ProductionActorsEdgeCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -1473,6 +1634,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ProductionActorsConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionActorsConnections match this filter + \\"\\"\\" + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere + } + input ProductionActorsConnectionSort { edge: ProductionActorsEdgeSort node: ActorSort @@ -1550,21 +1730,22 @@ describe("Interface Relationships", () => { AND: [ProductionActorsNodeAggregationWhereInput!] NOT: ProductionActorsNodeAggregationWhereInput OR: [ProductionActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ProductionActorsRelationship { @@ -1625,13 +1806,15 @@ describe("Interface Relationships", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -1643,47 +1826,48 @@ describe("Interface Relationships", () => { input ProductionUpdateInput { actors: [ProductionActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + actors: ActorRelationshipFilters actorsAggregate: ProductionActorsAggregateInput + actorsConnection: ProductionActorsConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Productions where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Productions where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Productions where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Productions where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -1693,31 +1877,31 @@ describe("Interface Relationships", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - episodes(limit: Int, offset: Int, options: EpisodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! + episodes(limit: Int, offset: Int, sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! episodesAggregate(where: EpisodeWhere): EpisodeAggregateSelection! episodesConnection(after: String, first: Int, sort: [EpisodeSort!], where: EpisodeWhere): EpisodesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! episodeCount: Int! - episodes(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: EpisodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! - episodesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: EpisodeWhere): SeriesEpisodeEpisodesAggregationSelection - episodesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesEpisodesConnectionSort!], where: SeriesEpisodesConnectionWhere): SeriesEpisodesConnection! + episodes(limit: Int, offset: Int, sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! + episodesAggregate(where: EpisodeWhere): SeriesEpisodeEpisodesAggregationSelection + episodesConnection(after: String, first: Int, sort: [SeriesEpisodesConnectionSort!], where: SeriesEpisodesConnectionWhere): SeriesEpisodesConnection! title: String! } @@ -1739,7 +1923,7 @@ describe("Interface Relationships", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1752,11 +1936,26 @@ describe("Interface Relationships", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { + \\"\\"\\" + Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + all: ProductionActorsConnectionWhere \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + Return Series where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -1773,21 +1972,22 @@ describe("Interface Relationships", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -1860,7 +2060,7 @@ describe("Interface Relationships", () => { AND: [SeriesEpisodesAggregateInput!] NOT: SeriesEpisodesAggregateInput OR: [SeriesEpisodesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1871,10 +2071,6 @@ describe("Interface Relationships", () => { input SeriesEpisodesConnectFieldInput { connect: [EpisodeConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: EpisodeConnectWhere } @@ -1884,6 +2080,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input SeriesEpisodesConnectionFilters { + \\"\\"\\" + Return Series where all of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + all: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where none of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + none: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where one of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + single: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where some of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + some: SeriesEpisodesConnectionWhere + } + input SeriesEpisodesConnectionSort { node: EpisodeSort } @@ -1918,26 +2133,27 @@ describe("Interface Relationships", () => { AND: [SeriesEpisodesNodeAggregationWhereInput!] NOT: SeriesEpisodesNodeAggregationWhereInput OR: [SeriesEpisodesNodeAggregationWhereInput!] - runtime_AVERAGE_EQUAL: Float - runtime_AVERAGE_GT: Float - runtime_AVERAGE_GTE: Float - runtime_AVERAGE_LT: Float - runtime_AVERAGE_LTE: Float - runtime_MAX_EQUAL: Int - runtime_MAX_GT: Int - runtime_MAX_GTE: Int - runtime_MAX_LT: Int - runtime_MAX_LTE: Int - runtime_MIN_EQUAL: Int - runtime_MIN_GT: Int - runtime_MIN_GTE: Int - runtime_MIN_LT: Int - runtime_MIN_LTE: Int - runtime_SUM_EQUAL: Int - runtime_SUM_GT: Int - runtime_SUM_GTE: Int - runtime_SUM_LT: Int - runtime_SUM_LTE: Int + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") } type SeriesEpisodesRelationship { @@ -1958,13 +2174,15 @@ describe("Interface Relationships", () => { where: SeriesEpisodesConnectionWhere } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] + input SeriesRelationshipFilters { + \\"\\"\\"Filter type where all of the related Series match this filter\\"\\"\\" + all: SeriesWhere + \\"\\"\\"Filter type where none of the related Series match this filter\\"\\"\\" + none: SeriesWhere + \\"\\"\\"Filter type where one of the related Series match this filter\\"\\"\\" + single: SeriesWhere + \\"\\"\\"Filter type where some of the related Series match this filter\\"\\"\\" + some: SeriesWhere } \\"\\"\\" @@ -1977,82 +2195,86 @@ describe("Interface Relationships", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodeCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodeCount_DECREMENT: Int - episodeCount_INCREMENT: Int - episodeCount_SET: Int + episodeCount: IntScalarMutations + episodeCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { decrement: ... } }' instead.\\") + episodeCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { increment: ... } }' instead.\\") + episodeCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodeCount: { set: ... } }' instead.\\") episodes: [SeriesEpisodesUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodeCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodeCount_EQ: Int - episodeCount_GT: Int - episodeCount_GTE: Int - episodeCount_IN: [Int!] - episodeCount_LT: Int - episodeCount_LTE: Int + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodeCount: IntScalarFilters + episodeCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { eq: ... }\\") + episodeCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gt: ... }\\") + episodeCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gte: ... }\\") + episodeCount_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { in: ... }\\") + episodeCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lt: ... }\\") + episodeCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lte: ... }\\") + episodes: EpisodeRelationshipFilters episodesAggregate: SeriesEpisodesAggregateInput + episodesConnection: SeriesEpisodesConnectionFilters \\"\\"\\" Return Series where all of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_ALL: SeriesEpisodesConnectionWhere + episodesConnection_ALL: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_NONE: SeriesEpisodesConnectionWhere + episodesConnection_NONE: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_SINGLE: SeriesEpisodesConnectionWhere + episodesConnection_SINGLE: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_SOME: SeriesEpisodesConnectionWhere + episodesConnection_SOME: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Episodes match this filter\\"\\"\\" - episodes_ALL: EpisodeWhere + episodes_ALL: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Episodes match this filter\\"\\"\\" - episodes_NONE: EpisodeWhere + episodes_NONE: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Episodes match this filter\\"\\"\\" - episodes_SINGLE: EpisodeWhere + episodes_SINGLE: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Episodes match this filter\\"\\"\\" - episodes_SOME: EpisodeWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + episodes_SOME: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -2068,6 +2290,27 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -2104,7 +2347,7 @@ describe("Interface Relationships", () => { const typeDefs = gql` type Episode @node { runtime: Int! - series: Series! @relationship(type: "HAS_EPISODE", direction: IN) + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -2160,26 +2403,27 @@ describe("Interface Relationships", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -2191,29 +2435,29 @@ describe("Interface Relationships", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -2221,7 +2465,7 @@ describe("Interface Relationships", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2243,6 +2487,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ProductionSort @@ -2280,21 +2543,22 @@ describe("Interface Relationships", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -2348,15 +2612,6 @@ describe("Interface Relationships", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - type ActorProductionActedInAggregationSelection { count: Int! edge: ActorProductionActedInEdgeAggregateSelection @@ -2371,6 +2626,17 @@ describe("Interface Relationships", () => { title: StringAggregateSelection! } + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -2380,45 +2646,47 @@ describe("Interface Relationships", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -2465,9 +2733,9 @@ describe("Interface Relationships", () => { type Episode { runtime: Int! - series(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): Series! - seriesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: SeriesWhere): EpisodeSeriesSeriesAggregationSelection - seriesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [EpisodeSeriesConnectionSort!], where: EpisodeSeriesConnectionWhere): EpisodeSeriesConnection! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): EpisodeSeriesSeriesAggregationSelection + seriesConnection(after: String, first: Int, sort: [EpisodeSeriesConnectionSort!], where: EpisodeSeriesConnectionWhere): EpisodeSeriesConnection! } type EpisodeAggregateSelection { @@ -2476,7 +2744,7 @@ describe("Interface Relationships", () => { } input EpisodeConnectInput { - series: EpisodeSeriesConnectFieldInput + series: [EpisodeSeriesConnectFieldInput!] } input EpisodeConnectWhere { @@ -2489,11 +2757,11 @@ describe("Interface Relationships", () => { } input EpisodeDeleteInput { - series: EpisodeSeriesDeleteFieldInput + series: [EpisodeSeriesDeleteFieldInput!] } input EpisodeDisconnectInput { - series: EpisodeSeriesDisconnectFieldInput + series: [EpisodeSeriesDisconnectFieldInput!] } type EpisodeEdge { @@ -2501,20 +2769,22 @@ describe("Interface Relationships", () => { node: Episode! } - input EpisodeOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more EpisodeSort objects to sort Episodes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [EpisodeSort!] + input EpisodeRelationshipFilters { + \\"\\"\\"Filter type where all of the related Episodes match this filter\\"\\"\\" + all: EpisodeWhere + \\"\\"\\"Filter type where none of the related Episodes match this filter\\"\\"\\" + none: EpisodeWhere + \\"\\"\\"Filter type where one of the related Episodes match this filter\\"\\"\\" + single: EpisodeWhere + \\"\\"\\"Filter type where some of the related Episodes match this filter\\"\\"\\" + some: EpisodeWhere } input EpisodeSeriesAggregateInput { AND: [EpisodeSeriesAggregateInput!] NOT: EpisodeSeriesAggregateInput OR: [EpisodeSeriesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2524,11 +2794,7 @@ describe("Interface Relationships", () => { } input EpisodeSeriesConnectFieldInput { - connect: SeriesConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [SeriesConnectInput!] where: SeriesConnectWhere } @@ -2538,6 +2804,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input EpisodeSeriesConnectionFilters { + \\"\\"\\" + Return Episodes where all of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + all: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where none of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + none: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where one of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + single: EpisodeSeriesConnectionWhere + \\"\\"\\" + Return Episodes where some of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + some: EpisodeSeriesConnectionWhere + } + input EpisodeSeriesConnectionSort { node: SeriesSort } @@ -2564,49 +2849,51 @@ describe("Interface Relationships", () => { } input EpisodeSeriesFieldInput { - connect: EpisodeSeriesConnectFieldInput - create: EpisodeSeriesCreateFieldInput + connect: [EpisodeSeriesConnectFieldInput!] + create: [EpisodeSeriesCreateFieldInput!] } input EpisodeSeriesNodeAggregationWhereInput { AND: [EpisodeSeriesNodeAggregationWhereInput!] NOT: EpisodeSeriesNodeAggregationWhereInput OR: [EpisodeSeriesNodeAggregationWhereInput!] - episodeCount_AVERAGE_EQUAL: Float - episodeCount_AVERAGE_GT: Float - episodeCount_AVERAGE_GTE: Float - episodeCount_AVERAGE_LT: Float - episodeCount_AVERAGE_LTE: Float - episodeCount_MAX_EQUAL: Int - episodeCount_MAX_GT: Int - episodeCount_MAX_GTE: Int - episodeCount_MAX_LT: Int - episodeCount_MAX_LTE: Int - episodeCount_MIN_EQUAL: Int - episodeCount_MIN_GT: Int - episodeCount_MIN_GTE: Int - episodeCount_MIN_LT: Int - episodeCount_MIN_LTE: Int - episodeCount_SUM_EQUAL: Int - episodeCount_SUM_GT: Int - episodeCount_SUM_GTE: Int - episodeCount_SUM_LT: Int - episodeCount_SUM_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + episodeCount: IntScalarAggregationFilters + episodeCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { eq: ... } } }' instead.\\") + episodeCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { gt: ... } } }' instead.\\") + episodeCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { gte: ... } } }' instead.\\") + episodeCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { lt: ... } } }' instead.\\") + episodeCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { average: { lte: ... } } }' instead.\\") + episodeCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { eq: ... } } }' instead.\\") + episodeCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { gt: ... } } }' instead.\\") + episodeCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { gte: ... } } }' instead.\\") + episodeCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { lt: ... } } }' instead.\\") + episodeCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { max: { lte: ... } } }' instead.\\") + episodeCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { eq: ... } } }' instead.\\") + episodeCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { gt: ... } } }' instead.\\") + episodeCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { gte: ... } } }' instead.\\") + episodeCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { lt: ... } } }' instead.\\") + episodeCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { min: { lte: ... } } }' instead.\\") + episodeCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { eq: ... } } }' instead.\\") + episodeCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { gt: ... } } }' instead.\\") + episodeCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { gte: ... } } }' instead.\\") + episodeCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { lt: ... } } }' instead.\\") + episodeCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeCount: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type EpisodeSeriesRelationship { @@ -2629,10 +2916,10 @@ describe("Interface Relationships", () => { } input EpisodeSeriesUpdateFieldInput { - connect: EpisodeSeriesConnectFieldInput - create: EpisodeSeriesCreateFieldInput - delete: EpisodeSeriesDeleteFieldInput - disconnect: EpisodeSeriesDisconnectFieldInput + connect: [EpisodeSeriesConnectFieldInput!] + create: [EpisodeSeriesCreateFieldInput!] + delete: [EpisodeSeriesDeleteFieldInput!] + disconnect: [EpisodeSeriesDisconnectFieldInput!] update: EpisodeSeriesUpdateConnectionInput where: EpisodeSeriesConnectionWhere } @@ -2645,27 +2932,51 @@ describe("Interface Relationships", () => { } input EpisodeUpdateInput { - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - series: EpisodeSeriesUpdateFieldInput + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + series: [EpisodeSeriesUpdateFieldInput!] } input EpisodeWhere { AND: [EpisodeWhere!] NOT: EpisodeWhere OR: [EpisodeWhere!] - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - series: SeriesWhere + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + series: SeriesRelationshipFilters seriesAggregate: EpisodeSeriesAggregateInput - seriesConnection: EpisodeSeriesConnectionWhere + seriesConnection: EpisodeSeriesConnectionFilters + \\"\\"\\" + Return Episodes where all of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_ALL: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where none of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_NONE: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where one of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_SINGLE: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Episodes where some of the related EpisodeSeriesConnections match this filter + \\"\\"\\" + seriesConnection_SOME: EpisodeSeriesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'seriesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Episodes where all of the related Series match this filter\\"\\"\\" + series_ALL: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { all: ... }' instead.\\") + \\"\\"\\"Return Episodes where none of the related Series match this filter\\"\\"\\" + series_NONE: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { none: ... }' instead.\\") + \\"\\"\\"Return Episodes where one of the related Series match this filter\\"\\"\\" + series_SINGLE: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { single: ... }' instead.\\") + \\"\\"\\"Return Episodes where some of the related Series match this filter\\"\\"\\" + series_SOME: SeriesWhere @deprecated(reason: \\"Please use the relevant generic filter 'series: { some: ... }' instead.\\") } type EpisodesConnection { @@ -2674,6 +2985,16 @@ describe("Interface Relationships", () => { totalCount: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -2681,10 +3002,35 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! runtime: Int! title: String! } @@ -2707,7 +3053,7 @@ describe("Interface Relationships", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2720,11 +3066,26 @@ describe("Interface Relationships", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -2741,21 +3102,22 @@ describe("Interface Relationships", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -2793,15 +3155,6 @@ describe("Interface Relationships", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -2812,56 +3165,58 @@ describe("Interface Relationships", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2894,7 +3249,7 @@ describe("Interface Relationships", () => { } interface Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! title: String! } @@ -2903,7 +3258,7 @@ describe("Interface Relationships", () => { AND: [ProductionActorsAggregateInput!] NOT: ProductionActorsAggregateInput OR: [ProductionActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2916,10 +3271,6 @@ describe("Interface Relationships", () => { input ProductionActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ProductionActorsEdgeCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2929,6 +3280,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ProductionActorsConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionActorsConnections match this filter + \\"\\"\\" + all: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere + } + input ProductionActorsConnectionSort { edge: ProductionActorsEdgeSort node: ActorSort @@ -3026,21 +3396,22 @@ describe("Interface Relationships", () => { AND: [ProductionActorsNodeAggregationWhereInput!] NOT: ProductionActorsNodeAggregationWhereInput OR: [ProductionActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ProductionActorsRelationship { @@ -3101,13 +3472,15 @@ describe("Interface Relationships", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -3119,47 +3492,48 @@ describe("Interface Relationships", () => { input ProductionUpdateInput { actors: [ProductionActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] + actors: ActorRelationshipFilters actorsAggregate: ProductionActorsAggregateInput + actorsConnection: ProductionActorsConnectionFilters \\"\\"\\" Return Productions where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Productions where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Productions where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Productions where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Productions where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Productions where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -3169,31 +3543,31 @@ describe("Interface Relationships", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - episodes(limit: Int, offset: Int, options: EpisodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! + episodes(limit: Int, offset: Int, sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! episodesAggregate(where: EpisodeWhere): EpisodeAggregateSelection! episodesConnection(after: String, first: Int, sort: [EpisodeSort!], where: EpisodeWhere): EpisodesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ProductionActorsConnectionSort!], where: ProductionActorsConnectionWhere): ProductionActorsConnection! episodeCount: Int! - episodes(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: EpisodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! - episodesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: EpisodeWhere): SeriesEpisodeEpisodesAggregationSelection - episodesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesEpisodesConnectionSort!], where: SeriesEpisodesConnectionWhere): SeriesEpisodesConnection! + episodes(limit: Int, offset: Int, sort: [EpisodeSort!], where: EpisodeWhere): [Episode!]! + episodesAggregate(where: EpisodeWhere): SeriesEpisodeEpisodesAggregationSelection + episodesConnection(after: String, first: Int, sort: [SeriesEpisodesConnectionSort!], where: SeriesEpisodesConnectionWhere): SeriesEpisodesConnection! title: String! } @@ -3215,7 +3589,7 @@ describe("Interface Relationships", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3228,11 +3602,26 @@ describe("Interface Relationships", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: StarredInCreateInput! + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { + \\"\\"\\" + Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + all: ProductionActorsConnectionWhere \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + Return Series where none of the related ProductionActorsConnections match this filter + \\"\\"\\" + none: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionActorsConnections match this filter + \\"\\"\\" + single: ProductionActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionActorsConnections match this filter + \\"\\"\\" + some: ProductionActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -3249,21 +3638,22 @@ describe("Interface Relationships", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -3336,7 +3726,7 @@ describe("Interface Relationships", () => { AND: [SeriesEpisodesAggregateInput!] NOT: SeriesEpisodesAggregateInput OR: [SeriesEpisodesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3347,10 +3737,6 @@ describe("Interface Relationships", () => { input SeriesEpisodesConnectFieldInput { connect: [EpisodeConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: EpisodeConnectWhere } @@ -3360,6 +3746,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input SeriesEpisodesConnectionFilters { + \\"\\"\\" + Return Series where all of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + all: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where none of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + none: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where one of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + single: SeriesEpisodesConnectionWhere + \\"\\"\\" + Return Series where some of the related SeriesEpisodesConnections match this filter + \\"\\"\\" + some: SeriesEpisodesConnectionWhere + } + input SeriesEpisodesConnectionSort { node: EpisodeSort } @@ -3394,26 +3799,27 @@ describe("Interface Relationships", () => { AND: [SeriesEpisodesNodeAggregationWhereInput!] NOT: SeriesEpisodesNodeAggregationWhereInput OR: [SeriesEpisodesNodeAggregationWhereInput!] - runtime_AVERAGE_EQUAL: Float - runtime_AVERAGE_GT: Float - runtime_AVERAGE_GTE: Float - runtime_AVERAGE_LT: Float - runtime_AVERAGE_LTE: Float - runtime_MAX_EQUAL: Int - runtime_MAX_GT: Int - runtime_MAX_GTE: Int - runtime_MAX_LT: Int - runtime_MAX_LTE: Int - runtime_MIN_EQUAL: Int - runtime_MIN_GT: Int - runtime_MIN_GTE: Int - runtime_MIN_LT: Int - runtime_MIN_LTE: Int - runtime_SUM_EQUAL: Int - runtime_SUM_GT: Int - runtime_SUM_GTE: Int - runtime_SUM_LT: Int - runtime_SUM_LTE: Int + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") } type SeriesEpisodesRelationship { @@ -3434,13 +3840,15 @@ describe("Interface Relationships", () => { where: SeriesEpisodesConnectionWhere } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] + input SeriesRelationshipFilters { + \\"\\"\\"Filter type where all of the related Series match this filter\\"\\"\\" + all: SeriesWhere + \\"\\"\\"Filter type where none of the related Series match this filter\\"\\"\\" + none: SeriesWhere + \\"\\"\\"Filter type where one of the related Series match this filter\\"\\"\\" + single: SeriesWhere + \\"\\"\\"Filter type where some of the related Series match this filter\\"\\"\\" + some: SeriesWhere } \\"\\"\\" @@ -3453,82 +3861,86 @@ describe("Interface Relationships", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodeCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodeCount_DECREMENT: Int - episodeCount_INCREMENT: Int - episodeCount_SET: Int + episodeCount: IntScalarMutations + episodeCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { decrement: ... } }' instead.\\") + episodeCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { increment: ... } }' instead.\\") + episodeCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodeCount: { set: ... } }' instead.\\") episodes: [SeriesEpisodesUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ProductionActorsConnectionWhere + actorsConnection_ALL: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ProductionActorsConnectionWhere + actorsConnection_NONE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ProductionActorsConnectionWhere + actorsConnection_SINGLE: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ProductionActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ProductionActorsConnectionWhere + actorsConnection_SOME: ProductionActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodeCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodeCount_EQ: Int - episodeCount_GT: Int - episodeCount_GTE: Int - episodeCount_IN: [Int!] - episodeCount_LT: Int - episodeCount_LTE: Int + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodeCount: IntScalarFilters + episodeCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { eq: ... }\\") + episodeCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gt: ... }\\") + episodeCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gte: ... }\\") + episodeCount_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { in: ... }\\") + episodeCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lt: ... }\\") + episodeCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lte: ... }\\") + episodes: EpisodeRelationshipFilters episodesAggregate: SeriesEpisodesAggregateInput + episodesConnection: SeriesEpisodesConnectionFilters \\"\\"\\" Return Series where all of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_ALL: SeriesEpisodesConnectionWhere + episodesConnection_ALL: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_NONE: SeriesEpisodesConnectionWhere + episodesConnection_NONE: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_SINGLE: SeriesEpisodesConnectionWhere + episodesConnection_SINGLE: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related SeriesEpisodesConnections match this filter \\"\\"\\" - episodesConnection_SOME: SeriesEpisodesConnectionWhere + episodesConnection_SOME: SeriesEpisodesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Episodes match this filter\\"\\"\\" - episodes_ALL: EpisodeWhere + episodes_ALL: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Episodes match this filter\\"\\"\\" - episodes_NONE: EpisodeWhere + episodes_NONE: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Episodes match this filter\\"\\"\\" - episodes_SINGLE: EpisodeWhere + episodes_SINGLE: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Episodes match this filter\\"\\"\\" - episodes_SOME: EpisodeWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + episodes_SOME: EpisodeWhere @deprecated(reason: \\"Please use the relevant generic filter 'episodes: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -3551,26 +3963,27 @@ describe("Interface Relationships", () => { AND: [StarredInAggregationWhereInput!] NOT: StarredInAggregationWhereInput OR: [StarredInAggregationWhereInput!] - seasons_AVERAGE_EQUAL: Float - seasons_AVERAGE_GT: Float - seasons_AVERAGE_GTE: Float - seasons_AVERAGE_LT: Float - seasons_AVERAGE_LTE: Float - seasons_MAX_EQUAL: Int - seasons_MAX_GT: Int - seasons_MAX_GTE: Int - seasons_MAX_LT: Int - seasons_MAX_LTE: Int - seasons_MIN_EQUAL: Int - seasons_MIN_GT: Int - seasons_MIN_GTE: Int - seasons_MIN_LT: Int - seasons_MIN_LTE: Int - seasons_SUM_EQUAL: Int - seasons_SUM_GT: Int - seasons_SUM_GTE: Int - seasons_SUM_LT: Int - seasons_SUM_LTE: Int + seasons: IntScalarAggregationFilters + seasons_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { average: { eq: ... } } }' instead.\\") + seasons_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { average: { gt: ... } } }' instead.\\") + seasons_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { average: { gte: ... } } }' instead.\\") + seasons_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { average: { lt: ... } } }' instead.\\") + seasons_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { average: { lte: ... } } }' instead.\\") + seasons_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { max: { eq: ... } } }' instead.\\") + seasons_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { max: { gt: ... } } }' instead.\\") + seasons_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { max: { gte: ... } } }' instead.\\") + seasons_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { max: { lt: ... } } }' instead.\\") + seasons_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { max: { lte: ... } } }' instead.\\") + seasons_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { min: { eq: ... } } }' instead.\\") + seasons_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { min: { gt: ... } } }' instead.\\") + seasons_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { min: { gte: ... } } }' instead.\\") + seasons_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { min: { lt: ... } } }' instead.\\") + seasons_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { min: { lte: ... } } }' instead.\\") + seasons_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { sum: { eq: ... } } }' instead.\\") + seasons_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { sum: { gt: ... } } }' instead.\\") + seasons_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { sum: { gte: ... } } }' instead.\\") + seasons_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { sum: { lt: ... } } }' instead.\\") + seasons_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'seasons: { sum: { lte: ... } } }' instead.\\") } input StarredInCreateInput { @@ -3582,23 +3995,23 @@ describe("Interface Relationships", () => { } input StarredInUpdateInput { - seasons: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - seasons_DECREMENT: Int - seasons_INCREMENT: Int - seasons_SET: Int + seasons: IntScalarMutations + seasons_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'seasons: { decrement: ... } }' instead.\\") + seasons_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'seasons: { increment: ... } }' instead.\\") + seasons_SET: Int @deprecated(reason: \\"Please use the generic mutation 'seasons: { set: ... } }' instead.\\") } input StarredInWhere { AND: [StarredInWhere!] NOT: StarredInWhere OR: [StarredInWhere!] - seasons: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - seasons_EQ: Int - seasons_GT: Int - seasons_GTE: Int - seasons_IN: [Int!] - seasons_LT: Int - seasons_LTE: Int + seasons: IntScalarFilters + seasons_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter seasons: { eq: ... }\\") + seasons_GT: Int @deprecated(reason: \\"Please use the relevant generic filter seasons: { gt: ... }\\") + seasons_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter seasons: { gte: ... }\\") + seasons_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter seasons: { in: ... }\\") + seasons_LT: Int @deprecated(reason: \\"Please use the relevant generic filter seasons: { lt: ... }\\") + seasons_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter seasons: { lte: ... }\\") } type StringAggregateSelection { @@ -3606,6 +4019,27 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -3723,9 +4157,29 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + interface Interface1 { field1: String! - interface2(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } @@ -3769,7 +4223,7 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2AggregateInput!] NOT: Interface1Interface2AggregateInput OR: [Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3788,6 +4242,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Interface1Interface2ConnectionSort { node: Interface2Sort } @@ -3815,21 +4288,22 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2NodeAggregationWhereInput!] NOT: Interface1Interface2NodeAggregationWhereInput OR: [Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } type Interface1Interface2Relationship { @@ -3850,13 +4324,15 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface1Sort objects to sort Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface1Sort!] + input Interface1RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface1s match this filter\\"\\"\\" + all: Interface1Where + \\"\\"\\"Filter type where none of the related Interface1s match this filter\\"\\"\\" + none: Interface1Where + \\"\\"\\"Filter type where one of the related Interface1s match this filter\\"\\"\\" + single: Interface1Where + \\"\\"\\"Filter type where some of the related Interface1s match this filter\\"\\"\\" + some: Interface1Where } \\"\\"\\" @@ -3867,8 +4343,8 @@ describe("Interface Relationships", () => { } input Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Interface1Interface2UpdateFieldInput!] } @@ -3876,47 +4352,48 @@ describe("Interface Relationships", () => { AND: [Interface1Where!] NOT: Interface1Where OR: [Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Interface1Interface2AggregateInput + interface2Connection: Interface1Interface2ConnectionFilters \\"\\"\\" Return Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") typename: [Interface1Implementation!] - typename_IN: [Interface1Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface1sConnection { @@ -3953,13 +4430,15 @@ describe("Interface Relationships", () => { Type2Interface2 } - input Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface2Sort objects to sort Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface2Sort!] + input Interface2RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface2s match this filter\\"\\"\\" + all: Interface2Where + \\"\\"\\"Filter type where none of the related Interface2s match this filter\\"\\"\\" + none: Interface2Where + \\"\\"\\"Filter type where one of the related Interface2s match this filter\\"\\"\\" + single: Interface2Where + \\"\\"\\"Filter type where some of the related Interface2s match this filter\\"\\"\\" + some: Interface2Where } \\"\\"\\" @@ -3970,22 +4449,21 @@ describe("Interface Relationships", () => { } input Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Interface2Where { AND: [Interface2Where!] NOT: Interface2Where OR: [Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") typename: [Interface2Implementation!] - typename_IN: [Interface2Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface2sConnection { @@ -4021,25 +4499,25 @@ describe("Interface Relationships", () => { } type Query { - interface1s(limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1s(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! interface1sAggregate(where: Interface1Where): Interface1AggregateSelection! interface1sConnection(after: String, first: Int, sort: [Interface1Sort!], where: Interface1Where): Interface1sConnection! - interface2s(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2s(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2sAggregate(where: Interface2Where): Interface2AggregateSelection! interface2sConnection(after: String, first: Int, sort: [Interface2Sort!], where: Interface2Where): Interface2sConnection! - type1Interface1s(limit: Int, offset: Int, options: Type1Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! + type1Interface1s(limit: Int, offset: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! type1Interface1sAggregate(where: Type1Interface1Where): Type1Interface1AggregateSelection! type1Interface1sConnection(after: String, first: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): Type1Interface1sConnection! - type1Interface2s(limit: Int, offset: Int, options: Type1Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! + type1Interface2s(limit: Int, offset: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! type1Interface2sAggregate(where: Type1Interface2Where): Type1Interface2AggregateSelection! type1Interface2sConnection(after: String, first: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): Type1Interface2sConnection! - type1s(limit: Int, offset: Int, options: Type1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Sort!], where: Type1Where): [Type1!]! + type1s(limit: Int, offset: Int, sort: [Type1Sort!], where: Type1Where): [Type1!]! type1sAggregate(where: Type1Where): Type1AggregateSelection! type1sConnection(after: String, first: Int, sort: [Type1Sort!], where: Type1Where): Type1sConnection! - type2Interface1s(limit: Int, offset: Int, options: Type2Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! + type2Interface1s(limit: Int, offset: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! type2Interface1sAggregate(where: Type2Interface1Where): Type2Interface1AggregateSelection! type2Interface1sConnection(after: String, first: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): Type2Interface1sConnection! - type2Interface2s(limit: Int, offset: Int, options: Type2Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! + type2Interface2s(limit: Int, offset: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! type2Interface2sAggregate(where: Type2Interface2Where): Type2Interface2AggregateSelection! type2Interface2sConnection(after: String, first: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): Type2Interface2sConnection! } @@ -4057,11 +4535,32 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Type1 { field1: String! - interface1(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! - interface1Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface1Where): Type1Interface1Interface1AggregationSelection - interface1Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! + interface1(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1Aggregate(where: Interface1Where): Type1Interface1Interface1AggregationSelection + interface1Connection(after: String, first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! } type Type1AggregateSelection { @@ -4085,16 +4584,16 @@ describe("Interface Relationships", () => { type Type1Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } input Type1Interface1AggregateInput { AND: [Type1Interface1AggregateInput!] NOT: Type1Interface1AggregateInput OR: [Type1Interface1AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4119,6 +4618,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Type1Interface1ConnectionFilters { + \\"\\"\\" + Return Type1s where all of the related Type1Interface1Connections match this filter + \\"\\"\\" + all: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where none of the related Type1Interface1Connections match this filter + \\"\\"\\" + none: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where one of the related Type1Interface1Connections match this filter + \\"\\"\\" + single: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where some of the related Type1Interface1Connections match this filter + \\"\\"\\" + some: Type1Interface1ConnectionWhere + } + input Type1Interface1ConnectionSort { node: Interface1Sort } @@ -4176,7 +4694,7 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2AggregateInput!] NOT: Type1Interface1Interface2AggregateInput OR: [Type1Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4189,6 +4707,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type1Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type1Interface1Interface2CreateFieldInput { node: Interface2CreateInput! } @@ -4219,21 +4756,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2NodeAggregationWhereInput!] NOT: Type1Interface1Interface2NodeAggregationWhereInput OR: [Type1Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type1Interface1Interface2UpdateConnectionInput { @@ -4253,30 +4791,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1NodeAggregationWhereInput!] NOT: Type1Interface1NodeAggregationWhereInput OR: [Type1Interface1NodeAggregationWhereInput!] - field1_AVERAGE_LENGTH_EQUAL: Float - field1_AVERAGE_LENGTH_GT: Float - field1_AVERAGE_LENGTH_GTE: Float - field1_AVERAGE_LENGTH_LT: Float - field1_AVERAGE_LENGTH_LTE: Float - field1_LONGEST_LENGTH_EQUAL: Int - field1_LONGEST_LENGTH_GT: Int - field1_LONGEST_LENGTH_GTE: Int - field1_LONGEST_LENGTH_LT: Int - field1_LONGEST_LENGTH_LTE: Int - field1_SHORTEST_LENGTH_EQUAL: Int - field1_SHORTEST_LENGTH_GT: Int - field1_SHORTEST_LENGTH_GTE: Int - field1_SHORTEST_LENGTH_LT: Int - field1_SHORTEST_LENGTH_LTE: Int - } - - input Type1Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface1Sort objects to sort Type1Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface1Sort!] + field1: StringScalarAggregationFilters + field1_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { eq: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gte: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { eq: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { eq: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lte: ... } } }' instead.\\") } type Type1Interface1Relationship { @@ -4305,8 +4835,8 @@ describe("Interface Relationships", () => { } input Type1Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type1Interface1Interface2UpdateFieldInput!] } @@ -4314,45 +4844,47 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Where!] NOT: Type1Interface1Where OR: [Type1Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type1Interface1Interface2AggregateInput + interface2Connection: Type1Interface1Interface2ConnectionFilters \\"\\"\\" Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type1Interface1sConnection { @@ -4379,15 +4911,6 @@ describe("Interface Relationships", () => { node: Type1Interface2! } - input Type1Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface2Sort objects to sort Type1Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface2Sort!] - } - \\"\\"\\" Fields to sort Type1Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type1Interface2Sort object. \\"\\"\\" @@ -4396,20 +4919,20 @@ describe("Interface Relationships", () => { } input Type1Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type1Interface2Where { AND: [Type1Interface2Where!] NOT: Type1Interface2Where OR: [Type1Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type1Interface2sConnection { @@ -4418,15 +4941,6 @@ describe("Interface Relationships", () => { totalCount: Int! } - input Type1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Sort objects to sort Type1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Sort!] - } - \\"\\"\\" Fields to sort Type1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type1Sort object. \\"\\"\\" @@ -4435,8 +4949,8 @@ describe("Interface Relationships", () => { } input Type1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface1: [Type1Interface1UpdateFieldInput!] } @@ -4444,37 +4958,39 @@ describe("Interface Relationships", () => { AND: [Type1Where!] NOT: Type1Where OR: [Type1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface1: Interface1RelationshipFilters interface1Aggregate: Type1Interface1AggregateInput + interface1Connection: Type1Interface1ConnectionFilters \\"\\"\\" Return Type1s where all of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_ALL: Type1Interface1ConnectionWhere + interface1Connection_ALL: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where none of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_NONE: Type1Interface1ConnectionWhere + interface1Connection_NONE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where one of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SINGLE: Type1Interface1ConnectionWhere + interface1Connection_SINGLE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where some of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SOME: Type1Interface1ConnectionWhere + interface1Connection_SOME: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Type1s where all of the related Interface1s match this filter\\"\\"\\" - interface1_ALL: Interface1Where + interface1_ALL: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { all: ... }' instead.\\") \\"\\"\\"Return Type1s where none of the related Interface1s match this filter\\"\\"\\" - interface1_NONE: Interface1Where + interface1_NONE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { none: ... }' instead.\\") \\"\\"\\"Return Type1s where one of the related Interface1s match this filter\\"\\"\\" - interface1_SINGLE: Interface1Where + interface1_SINGLE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { single: ... }' instead.\\") \\"\\"\\"Return Type1s where some of the related Interface1s match this filter\\"\\"\\" - interface1_SOME: Interface1Where + interface1_SOME: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { some: ... }' instead.\\") } type Type1sConnection { @@ -4485,9 +5001,9 @@ describe("Interface Relationships", () => { type Type2Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } type Type2Interface1AggregateSelection { @@ -4513,7 +5029,7 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2AggregateInput!] NOT: Type2Interface1Interface2AggregateInput OR: [Type2Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4526,6 +5042,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type2Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type2Interface1Interface2CreateFieldInput { node: Interface2CreateInput! } @@ -4556,21 +5091,22 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2NodeAggregationWhereInput!] NOT: Type2Interface1Interface2NodeAggregationWhereInput OR: [Type2Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type2Interface1Interface2UpdateConnectionInput { @@ -4586,15 +5122,6 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Type2Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface1Sort objects to sort Type2Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface1Sort!] - } - \\"\\"\\" Fields to sort Type2Interface1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface1Sort object. \\"\\"\\" @@ -4603,8 +5130,8 @@ describe("Interface Relationships", () => { } input Type2Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type2Interface1Interface2UpdateFieldInput!] } @@ -4612,45 +5139,47 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Where!] NOT: Type2Interface1Where OR: [Type2Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type2Interface1Interface2AggregateInput + interface2Connection: Type2Interface1Interface2ConnectionFilters \\"\\"\\" Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type2Interface1sConnection { @@ -4677,15 +5206,6 @@ describe("Interface Relationships", () => { node: Type2Interface2! } - input Type2Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface2Sort objects to sort Type2Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface2Sort!] - } - \\"\\"\\" Fields to sort Type2Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface2Sort object. \\"\\"\\" @@ -4694,20 +5214,20 @@ describe("Interface Relationships", () => { } input Type2Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type2Interface2Where { AND: [Type2Interface2Where!] NOT: Type2Interface2Where OR: [Type2Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type2Interface2sConnection { @@ -4847,6 +5367,16 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -4854,9 +5384,34 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + interface Interface1 { field1: String! - interface2(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } @@ -4900,7 +5455,7 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2AggregateInput!] NOT: Interface1Interface2AggregateInput OR: [Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4921,6 +5476,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Interface1Interface2ConnectionSort { edge: Interface1Interface2EdgeSort node: Interface2Sort @@ -4996,21 +5570,22 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2NodeAggregationWhereInput!] NOT: Interface1Interface2NodeAggregationWhereInput OR: [Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } type Interface1Interface2Relationship { @@ -5035,13 +5610,15 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface1Sort objects to sort Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface1Sort!] + input Interface1RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface1s match this filter\\"\\"\\" + all: Interface1Where + \\"\\"\\"Filter type where none of the related Interface1s match this filter\\"\\"\\" + none: Interface1Where + \\"\\"\\"Filter type where one of the related Interface1s match this filter\\"\\"\\" + single: Interface1Where + \\"\\"\\"Filter type where some of the related Interface1s match this filter\\"\\"\\" + some: Interface1Where } \\"\\"\\" @@ -5052,8 +5629,8 @@ describe("Interface Relationships", () => { } input Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Interface1Interface2UpdateFieldInput!] } @@ -5061,47 +5638,48 @@ describe("Interface Relationships", () => { AND: [Interface1Where!] NOT: Interface1Where OR: [Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Interface1Interface2AggregateInput + interface2Connection: Interface1Interface2ConnectionFilters \\"\\"\\" Return Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") typename: [Interface1Implementation!] - typename_IN: [Interface1Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface1sConnection { @@ -5138,13 +5716,15 @@ describe("Interface Relationships", () => { Type2Interface2 } - input Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface2Sort objects to sort Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface2Sort!] + input Interface2RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface2s match this filter\\"\\"\\" + all: Interface2Where + \\"\\"\\"Filter type where none of the related Interface2s match this filter\\"\\"\\" + none: Interface2Where + \\"\\"\\"Filter type where one of the related Interface2s match this filter\\"\\"\\" + single: Interface2Where + \\"\\"\\"Filter type where some of the related Interface2s match this filter\\"\\"\\" + some: Interface2Where } \\"\\"\\" @@ -5155,22 +5735,21 @@ describe("Interface Relationships", () => { } input Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Interface2Where { AND: [Interface2Where!] NOT: Interface2Where OR: [Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") typename: [Interface2Implementation!] - typename_IN: [Interface2Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface2sConnection { @@ -5218,26 +5797,27 @@ describe("Interface Relationships", () => { AND: [PropsAggregationWhereInput!] NOT: PropsAggregationWhereInput OR: [PropsAggregationWhereInput!] - propsField_AVERAGE_EQUAL: Float - propsField_AVERAGE_GT: Float - propsField_AVERAGE_GTE: Float - propsField_AVERAGE_LT: Float - propsField_AVERAGE_LTE: Float - propsField_MAX_EQUAL: Int - propsField_MAX_GT: Int - propsField_MAX_GTE: Int - propsField_MAX_LT: Int - propsField_MAX_LTE: Int - propsField_MIN_EQUAL: Int - propsField_MIN_GT: Int - propsField_MIN_GTE: Int - propsField_MIN_LT: Int - propsField_MIN_LTE: Int - propsField_SUM_EQUAL: Int - propsField_SUM_GT: Int - propsField_SUM_GTE: Int - propsField_SUM_LT: Int - propsField_SUM_LTE: Int + propsField: IntScalarAggregationFilters + propsField_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { average: { eq: ... } } }' instead.\\") + propsField_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { average: { gt: ... } } }' instead.\\") + propsField_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { average: { gte: ... } } }' instead.\\") + propsField_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { average: { lt: ... } } }' instead.\\") + propsField_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { average: { lte: ... } } }' instead.\\") + propsField_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { max: { eq: ... } } }' instead.\\") + propsField_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { max: { gt: ... } } }' instead.\\") + propsField_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { max: { gte: ... } } }' instead.\\") + propsField_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { max: { lt: ... } } }' instead.\\") + propsField_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { max: { lte: ... } } }' instead.\\") + propsField_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { min: { eq: ... } } }' instead.\\") + propsField_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { min: { gt: ... } } }' instead.\\") + propsField_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { min: { gte: ... } } }' instead.\\") + propsField_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { min: { lt: ... } } }' instead.\\") + propsField_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { min: { lte: ... } } }' instead.\\") + propsField_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { sum: { eq: ... } } }' instead.\\") + propsField_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { sum: { gt: ... } } }' instead.\\") + propsField_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { sum: { gte: ... } } }' instead.\\") + propsField_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { sum: { lt: ... } } }' instead.\\") + propsField_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'propsField: { sum: { lte: ... } } }' instead.\\") } input PropsCreateInput { @@ -5249,45 +5829,45 @@ describe("Interface Relationships", () => { } input PropsUpdateInput { - propsField: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - propsField_DECREMENT: Int - propsField_INCREMENT: Int - propsField_SET: Int + propsField: IntScalarMutations + propsField_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'propsField: { decrement: ... } }' instead.\\") + propsField_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'propsField: { increment: ... } }' instead.\\") + propsField_SET: Int @deprecated(reason: \\"Please use the generic mutation 'propsField: { set: ... } }' instead.\\") } input PropsWhere { AND: [PropsWhere!] NOT: PropsWhere OR: [PropsWhere!] - propsField: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - propsField_EQ: Int - propsField_GT: Int - propsField_GTE: Int - propsField_IN: [Int!] - propsField_LT: Int - propsField_LTE: Int + propsField: IntScalarFilters + propsField_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter propsField: { eq: ... }\\") + propsField_GT: Int @deprecated(reason: \\"Please use the relevant generic filter propsField: { gt: ... }\\") + propsField_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter propsField: { gte: ... }\\") + propsField_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter propsField: { in: ... }\\") + propsField_LT: Int @deprecated(reason: \\"Please use the relevant generic filter propsField: { lt: ... }\\") + propsField_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter propsField: { lte: ... }\\") } type Query { - interface1s(limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1s(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! interface1sAggregate(where: Interface1Where): Interface1AggregateSelection! interface1sConnection(after: String, first: Int, sort: [Interface1Sort!], where: Interface1Where): Interface1sConnection! - interface2s(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2s(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2sAggregate(where: Interface2Where): Interface2AggregateSelection! interface2sConnection(after: String, first: Int, sort: [Interface2Sort!], where: Interface2Where): Interface2sConnection! - type1Interface1s(limit: Int, offset: Int, options: Type1Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! + type1Interface1s(limit: Int, offset: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! type1Interface1sAggregate(where: Type1Interface1Where): Type1Interface1AggregateSelection! type1Interface1sConnection(after: String, first: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): Type1Interface1sConnection! - type1Interface2s(limit: Int, offset: Int, options: Type1Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! + type1Interface2s(limit: Int, offset: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! type1Interface2sAggregate(where: Type1Interface2Where): Type1Interface2AggregateSelection! type1Interface2sConnection(after: String, first: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): Type1Interface2sConnection! - type1s(limit: Int, offset: Int, options: Type1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Sort!], where: Type1Where): [Type1!]! + type1s(limit: Int, offset: Int, sort: [Type1Sort!], where: Type1Where): [Type1!]! type1sAggregate(where: Type1Where): Type1AggregateSelection! type1sConnection(after: String, first: Int, sort: [Type1Sort!], where: Type1Where): Type1sConnection! - type2Interface1s(limit: Int, offset: Int, options: Type2Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! + type2Interface1s(limit: Int, offset: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! type2Interface1sAggregate(where: Type2Interface1Where): Type2Interface1AggregateSelection! type2Interface1sConnection(after: String, first: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): Type2Interface1sConnection! - type2Interface2s(limit: Int, offset: Int, options: Type2Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! + type2Interface2s(limit: Int, offset: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! type2Interface2sAggregate(where: Type2Interface2Where): Type2Interface2AggregateSelection! type2Interface2sConnection(after: String, first: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): Type2Interface2sConnection! } @@ -5305,11 +5885,32 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Type1 { field1: String! - interface1(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! - interface1Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface1Where): Type1Interface1Interface1AggregationSelection - interface1Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! + interface1(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1Aggregate(where: Interface1Where): Type1Interface1Interface1AggregationSelection + interface1Connection(after: String, first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! } type Type1AggregateSelection { @@ -5333,16 +5934,16 @@ describe("Interface Relationships", () => { type Type1Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } input Type1Interface1AggregateInput { AND: [Type1Interface1AggregateInput!] NOT: Type1Interface1AggregateInput OR: [Type1Interface1AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5367,6 +5968,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Type1Interface1ConnectionFilters { + \\"\\"\\" + Return Type1s where all of the related Type1Interface1Connections match this filter + \\"\\"\\" + all: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where none of the related Type1Interface1Connections match this filter + \\"\\"\\" + none: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where one of the related Type1Interface1Connections match this filter + \\"\\"\\" + single: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where some of the related Type1Interface1Connections match this filter + \\"\\"\\" + some: Type1Interface1ConnectionWhere + } + input Type1Interface1ConnectionSort { node: Interface1Sort } @@ -5424,7 +6044,7 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2AggregateInput!] NOT: Type1Interface1Interface2AggregateInput OR: [Type1Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5439,6 +6059,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type1Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type1Interface1Interface2CreateFieldInput { edge: PropsCreateInput! node: Interface2CreateInput! @@ -5475,21 +6114,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2NodeAggregationWhereInput!] NOT: Type1Interface1Interface2NodeAggregationWhereInput OR: [Type1Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type1Interface1Interface2UpdateConnectionInput { @@ -5510,30 +6150,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1NodeAggregationWhereInput!] NOT: Type1Interface1NodeAggregationWhereInput OR: [Type1Interface1NodeAggregationWhereInput!] - field1_AVERAGE_LENGTH_EQUAL: Float - field1_AVERAGE_LENGTH_GT: Float - field1_AVERAGE_LENGTH_GTE: Float - field1_AVERAGE_LENGTH_LT: Float - field1_AVERAGE_LENGTH_LTE: Float - field1_LONGEST_LENGTH_EQUAL: Int - field1_LONGEST_LENGTH_GT: Int - field1_LONGEST_LENGTH_GTE: Int - field1_LONGEST_LENGTH_LT: Int - field1_LONGEST_LENGTH_LTE: Int - field1_SHORTEST_LENGTH_EQUAL: Int - field1_SHORTEST_LENGTH_GT: Int - field1_SHORTEST_LENGTH_GTE: Int - field1_SHORTEST_LENGTH_LT: Int - field1_SHORTEST_LENGTH_LTE: Int - } - - input Type1Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface1Sort objects to sort Type1Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface1Sort!] + field1: StringScalarAggregationFilters + field1_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { eq: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gte: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { eq: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { eq: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lte: ... } } }' instead.\\") } type Type1Interface1Relationship { @@ -5562,8 +6194,8 @@ describe("Interface Relationships", () => { } input Type1Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type1Interface1Interface2UpdateFieldInput!] } @@ -5571,45 +6203,47 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Where!] NOT: Type1Interface1Where OR: [Type1Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type1Interface1Interface2AggregateInput + interface2Connection: Type1Interface1Interface2ConnectionFilters \\"\\"\\" Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type1Interface1sConnection { @@ -5636,15 +6270,6 @@ describe("Interface Relationships", () => { node: Type1Interface2! } - input Type1Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface2Sort objects to sort Type1Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface2Sort!] - } - \\"\\"\\" Fields to sort Type1Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type1Interface2Sort object. \\"\\"\\" @@ -5653,20 +6278,20 @@ describe("Interface Relationships", () => { } input Type1Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type1Interface2Where { AND: [Type1Interface2Where!] NOT: Type1Interface2Where OR: [Type1Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type1Interface2sConnection { @@ -5675,15 +6300,6 @@ describe("Interface Relationships", () => { totalCount: Int! } - input Type1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Sort objects to sort Type1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Sort!] - } - \\"\\"\\" Fields to sort Type1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type1Sort object. \\"\\"\\" @@ -5692,8 +6308,8 @@ describe("Interface Relationships", () => { } input Type1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface1: [Type1Interface1UpdateFieldInput!] } @@ -5701,37 +6317,39 @@ describe("Interface Relationships", () => { AND: [Type1Where!] NOT: Type1Where OR: [Type1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface1: Interface1RelationshipFilters interface1Aggregate: Type1Interface1AggregateInput + interface1Connection: Type1Interface1ConnectionFilters \\"\\"\\" Return Type1s where all of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_ALL: Type1Interface1ConnectionWhere + interface1Connection_ALL: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where none of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_NONE: Type1Interface1ConnectionWhere + interface1Connection_NONE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where one of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SINGLE: Type1Interface1ConnectionWhere + interface1Connection_SINGLE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where some of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SOME: Type1Interface1ConnectionWhere + interface1Connection_SOME: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Type1s where all of the related Interface1s match this filter\\"\\"\\" - interface1_ALL: Interface1Where + interface1_ALL: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { all: ... }' instead.\\") \\"\\"\\"Return Type1s where none of the related Interface1s match this filter\\"\\"\\" - interface1_NONE: Interface1Where + interface1_NONE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { none: ... }' instead.\\") \\"\\"\\"Return Type1s where one of the related Interface1s match this filter\\"\\"\\" - interface1_SINGLE: Interface1Where + interface1_SINGLE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { single: ... }' instead.\\") \\"\\"\\"Return Type1s where some of the related Interface1s match this filter\\"\\"\\" - interface1_SOME: Interface1Where + interface1_SOME: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { some: ... }' instead.\\") } type Type1sConnection { @@ -5742,9 +6360,9 @@ describe("Interface Relationships", () => { type Type2Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } type Type2Interface1AggregateSelection { @@ -5770,7 +6388,7 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2AggregateInput!] NOT: Type2Interface1Interface2AggregateInput OR: [Type2Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -5785,6 +6403,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type2Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type2Interface1Interface2CreateFieldInput { edge: PropsCreateInput! node: Interface2CreateInput! @@ -5821,21 +6458,22 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2NodeAggregationWhereInput!] NOT: Type2Interface1Interface2NodeAggregationWhereInput OR: [Type2Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type2Interface1Interface2UpdateConnectionInput { @@ -5852,15 +6490,6 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Type2Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface1Sort objects to sort Type2Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface1Sort!] - } - \\"\\"\\" Fields to sort Type2Interface1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface1Sort object. \\"\\"\\" @@ -5869,8 +6498,8 @@ describe("Interface Relationships", () => { } input Type2Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type2Interface1Interface2UpdateFieldInput!] } @@ -5878,45 +6507,47 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Where!] NOT: Type2Interface1Where OR: [Type2Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type2Interface1Interface2AggregateInput + interface2Connection: Type2Interface1Interface2ConnectionFilters \\"\\"\\" Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type2Interface1sConnection { @@ -5943,15 +6574,6 @@ describe("Interface Relationships", () => { node: Type2Interface2! } - input Type2Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface2Sort objects to sort Type2Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface2Sort!] - } - \\"\\"\\" Fields to sort Type2Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface2Sort object. \\"\\"\\" @@ -5960,20 +6582,20 @@ describe("Interface Relationships", () => { } input Type2Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type2Interface2Where { AND: [Type2Interface2Where!] NOT: Type2Interface2Where OR: [Type2Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type2Interface2sConnection { @@ -6119,6 +6741,16 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -6126,9 +6758,34 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + interface Interface1 { field1: String! - interface2(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } @@ -6172,7 +6829,7 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2AggregateInput!] NOT: Interface1Interface2AggregateInput OR: [Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6193,6 +6850,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Interface1Interface2ConnectionSort { edge: Interface1Interface2EdgeSort node: Interface2Sort @@ -6288,21 +6964,22 @@ describe("Interface Relationships", () => { AND: [Interface1Interface2NodeAggregationWhereInput!] NOT: Interface1Interface2NodeAggregationWhereInput OR: [Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } type Interface1Interface2Relationship { @@ -6327,13 +7004,15 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface1Sort objects to sort Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface1Sort!] + input Interface1RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface1s match this filter\\"\\"\\" + all: Interface1Where + \\"\\"\\"Filter type where none of the related Interface1s match this filter\\"\\"\\" + none: Interface1Where + \\"\\"\\"Filter type where one of the related Interface1s match this filter\\"\\"\\" + single: Interface1Where + \\"\\"\\"Filter type where some of the related Interface1s match this filter\\"\\"\\" + some: Interface1Where } \\"\\"\\" @@ -6344,8 +7023,8 @@ describe("Interface Relationships", () => { } input Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Interface1Interface2UpdateFieldInput!] } @@ -6353,47 +7032,48 @@ describe("Interface Relationships", () => { AND: [Interface1Where!] NOT: Interface1Where OR: [Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Interface1Interface2AggregateInput + interface2Connection: Interface1Interface2ConnectionFilters \\"\\"\\" Return Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") typename: [Interface1Implementation!] - typename_IN: [Interface1Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface1sConnection { @@ -6430,13 +7110,15 @@ describe("Interface Relationships", () => { Type2Interface2 } - input Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Interface2Sort objects to sort Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Interface2Sort!] + input Interface2RelationshipFilters { + \\"\\"\\"Filter type where all of the related Interface2s match this filter\\"\\"\\" + all: Interface2Where + \\"\\"\\"Filter type where none of the related Interface2s match this filter\\"\\"\\" + none: Interface2Where + \\"\\"\\"Filter type where one of the related Interface2s match this filter\\"\\"\\" + single: Interface2Where + \\"\\"\\"Filter type where some of the related Interface2s match this filter\\"\\"\\" + some: Interface2Where } \\"\\"\\" @@ -6447,22 +7129,21 @@ describe("Interface Relationships", () => { } input Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Interface2Where { AND: [Interface2Where!] NOT: Interface2Where OR: [Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") typename: [Interface2Implementation!] - typename_IN: [Interface2Implementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type Interface2sConnection { @@ -6498,25 +7179,25 @@ describe("Interface Relationships", () => { } type Query { - interface1s(limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1s(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! interface1sAggregate(where: Interface1Where): Interface1AggregateSelection! interface1sConnection(after: String, first: Int, sort: [Interface1Sort!], where: Interface1Where): Interface1sConnection! - interface2s(limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2s(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! interface2sAggregate(where: Interface2Where): Interface2AggregateSelection! interface2sConnection(after: String, first: Int, sort: [Interface2Sort!], where: Interface2Where): Interface2sConnection! - type1Interface1s(limit: Int, offset: Int, options: Type1Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! + type1Interface1s(limit: Int, offset: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): [Type1Interface1!]! type1Interface1sAggregate(where: Type1Interface1Where): Type1Interface1AggregateSelection! type1Interface1sConnection(after: String, first: Int, sort: [Type1Interface1Sort!], where: Type1Interface1Where): Type1Interface1sConnection! - type1Interface2s(limit: Int, offset: Int, options: Type1Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! + type1Interface2s(limit: Int, offset: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): [Type1Interface2!]! type1Interface2sAggregate(where: Type1Interface2Where): Type1Interface2AggregateSelection! type1Interface2sConnection(after: String, first: Int, sort: [Type1Interface2Sort!], where: Type1Interface2Where): Type1Interface2sConnection! - type1s(limit: Int, offset: Int, options: Type1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type1Sort!], where: Type1Where): [Type1!]! + type1s(limit: Int, offset: Int, sort: [Type1Sort!], where: Type1Where): [Type1!]! type1sAggregate(where: Type1Where): Type1AggregateSelection! type1sConnection(after: String, first: Int, sort: [Type1Sort!], where: Type1Where): Type1sConnection! - type2Interface1s(limit: Int, offset: Int, options: Type2Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! + type2Interface1s(limit: Int, offset: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): [Type2Interface1!]! type2Interface1sAggregate(where: Type2Interface1Where): Type2Interface1AggregateSelection! type2Interface1sConnection(after: String, first: Int, sort: [Type2Interface1Sort!], where: Type2Interface1Where): Type2Interface1sConnection! - type2Interface2s(limit: Int, offset: Int, options: Type2Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! + type2Interface2s(limit: Int, offset: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): [Type2Interface2!]! type2Interface2sAggregate(where: Type2Interface2Where): Type2Interface2AggregateSelection! type2Interface2sConnection(after: String, first: Int, sort: [Type2Interface2Sort!], where: Type2Interface2Where): Type2Interface2sConnection! } @@ -6534,15 +7215,36 @@ describe("Interface Relationships", () => { shortest: String } - type Type1 { - field1: String! - interface1(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! - interface1Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface1Where): Type1Interface1Interface1AggregationSelection - interface1Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters } - type Type1AggregateSelection { - count: Int! + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type Type1 { + field1: String! + interface1(limit: Int, offset: Int, sort: [Interface1Sort!], where: Interface1Where): [Interface1!]! + interface1Aggregate(where: Interface1Where): Type1Interface1Interface1AggregationSelection + interface1Connection(after: String, first: Int, sort: [Type1Interface1ConnectionSort!], where: Type1Interface1ConnectionWhere): Type1Interface1Connection! + } + + type Type1AggregateSelection { + count: Int! field1: StringAggregateSelection! } @@ -6562,16 +7264,16 @@ describe("Interface Relationships", () => { type Type1Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type1Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } input Type1Interface1AggregateInput { AND: [Type1Interface1AggregateInput!] NOT: Type1Interface1AggregateInput OR: [Type1Interface1AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6596,6 +7298,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input Type1Interface1ConnectionFilters { + \\"\\"\\" + Return Type1s where all of the related Type1Interface1Connections match this filter + \\"\\"\\" + all: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where none of the related Type1Interface1Connections match this filter + \\"\\"\\" + none: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where one of the related Type1Interface1Connections match this filter + \\"\\"\\" + single: Type1Interface1ConnectionWhere + \\"\\"\\" + Return Type1s where some of the related Type1Interface1Connections match this filter + \\"\\"\\" + some: Type1Interface1ConnectionWhere + } + input Type1Interface1ConnectionSort { node: Interface1Sort } @@ -6653,7 +7374,7 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2AggregateInput!] NOT: Type1Interface1Interface2AggregateInput OR: [Type1Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -6668,6 +7389,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type1Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type1Interface1Interface2CreateFieldInput { edge: Type1PropsCreateInput! node: Interface2CreateInput! @@ -6704,21 +7444,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Interface2NodeAggregationWhereInput!] NOT: Type1Interface1Interface2NodeAggregationWhereInput OR: [Type1Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type1Interface1Interface2UpdateConnectionInput { @@ -6739,30 +7480,22 @@ describe("Interface Relationships", () => { AND: [Type1Interface1NodeAggregationWhereInput!] NOT: Type1Interface1NodeAggregationWhereInput OR: [Type1Interface1NodeAggregationWhereInput!] - field1_AVERAGE_LENGTH_EQUAL: Float - field1_AVERAGE_LENGTH_GT: Float - field1_AVERAGE_LENGTH_GTE: Float - field1_AVERAGE_LENGTH_LT: Float - field1_AVERAGE_LENGTH_LTE: Float - field1_LONGEST_LENGTH_EQUAL: Int - field1_LONGEST_LENGTH_GT: Int - field1_LONGEST_LENGTH_GTE: Int - field1_LONGEST_LENGTH_LT: Int - field1_LONGEST_LENGTH_LTE: Int - field1_SHORTEST_LENGTH_EQUAL: Int - field1_SHORTEST_LENGTH_GT: Int - field1_SHORTEST_LENGTH_GTE: Int - field1_SHORTEST_LENGTH_LT: Int - field1_SHORTEST_LENGTH_LTE: Int - } - - input Type1Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface1Sort objects to sort Type1Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface1Sort!] + field1: StringScalarAggregationFilters + field1_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { eq: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { gte: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lt: ... } } }' instead.\\") + field1_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field1: { averageLength: { lte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { eq: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { gte: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lt: ... } } }' instead.\\") + field1_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { longestLength: { lte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { eq: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { gte: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lt: ... } } }' instead.\\") + field1_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field1: { shortestLength: { lte: ... } } }' instead.\\") } type Type1Interface1Relationship { @@ -6791,8 +7524,8 @@ describe("Interface Relationships", () => { } input Type1Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type1Interface1Interface2UpdateFieldInput!] } @@ -6800,45 +7533,47 @@ describe("Interface Relationships", () => { AND: [Type1Interface1Where!] NOT: Type1Interface1Where OR: [Type1Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type1Interface1Interface2AggregateInput + interface2Connection: Type1Interface1Interface2ConnectionFilters \\"\\"\\" Return Type1Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type1Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type1Interface1sConnection { @@ -6865,15 +7600,6 @@ describe("Interface Relationships", () => { node: Type1Interface2! } - input Type1Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Interface2Sort objects to sort Type1Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Interface2Sort!] - } - \\"\\"\\" Fields to sort Type1Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type1Interface2Sort object. \\"\\"\\" @@ -6882,20 +7608,20 @@ describe("Interface Relationships", () => { } input Type1Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type1Interface2Where { AND: [Type1Interface2Where!] NOT: Type1Interface2Where OR: [Type1Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type1Interface2sConnection { @@ -6904,15 +7630,6 @@ describe("Interface Relationships", () => { totalCount: Int! } - input Type1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type1Sort objects to sort Type1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type1Sort!] - } - \\"\\"\\" The edge properties for the following fields: * Type1Interface1.interface2 @@ -6925,26 +7642,27 @@ describe("Interface Relationships", () => { AND: [Type1PropsAggregationWhereInput!] NOT: Type1PropsAggregationWhereInput OR: [Type1PropsAggregationWhereInput!] - type1Field_AVERAGE_EQUAL: Float - type1Field_AVERAGE_GT: Float - type1Field_AVERAGE_GTE: Float - type1Field_AVERAGE_LT: Float - type1Field_AVERAGE_LTE: Float - type1Field_MAX_EQUAL: Int - type1Field_MAX_GT: Int - type1Field_MAX_GTE: Int - type1Field_MAX_LT: Int - type1Field_MAX_LTE: Int - type1Field_MIN_EQUAL: Int - type1Field_MIN_GT: Int - type1Field_MIN_GTE: Int - type1Field_MIN_LT: Int - type1Field_MIN_LTE: Int - type1Field_SUM_EQUAL: Int - type1Field_SUM_GT: Int - type1Field_SUM_GTE: Int - type1Field_SUM_LT: Int - type1Field_SUM_LTE: Int + type1Field: IntScalarAggregationFilters + type1Field_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { average: { eq: ... } } }' instead.\\") + type1Field_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { average: { gt: ... } } }' instead.\\") + type1Field_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { average: { gte: ... } } }' instead.\\") + type1Field_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { average: { lt: ... } } }' instead.\\") + type1Field_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { average: { lte: ... } } }' instead.\\") + type1Field_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { max: { eq: ... } } }' instead.\\") + type1Field_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { max: { gt: ... } } }' instead.\\") + type1Field_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { max: { gte: ... } } }' instead.\\") + type1Field_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { max: { lt: ... } } }' instead.\\") + type1Field_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { max: { lte: ... } } }' instead.\\") + type1Field_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { min: { eq: ... } } }' instead.\\") + type1Field_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { min: { gt: ... } } }' instead.\\") + type1Field_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { min: { gte: ... } } }' instead.\\") + type1Field_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { min: { lt: ... } } }' instead.\\") + type1Field_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { min: { lte: ... } } }' instead.\\") + type1Field_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { sum: { eq: ... } } }' instead.\\") + type1Field_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { sum: { gt: ... } } }' instead.\\") + type1Field_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { sum: { gte: ... } } }' instead.\\") + type1Field_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { sum: { lt: ... } } }' instead.\\") + type1Field_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type1Field: { sum: { lte: ... } } }' instead.\\") } input Type1PropsCreateInput { @@ -6956,23 +7674,23 @@ describe("Interface Relationships", () => { } input Type1PropsUpdateInput { - type1Field: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - type1Field_DECREMENT: Int - type1Field_INCREMENT: Int - type1Field_SET: Int + type1Field: IntScalarMutations + type1Field_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'type1Field: { decrement: ... } }' instead.\\") + type1Field_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'type1Field: { increment: ... } }' instead.\\") + type1Field_SET: Int @deprecated(reason: \\"Please use the generic mutation 'type1Field: { set: ... } }' instead.\\") } input Type1PropsWhere { AND: [Type1PropsWhere!] NOT: Type1PropsWhere OR: [Type1PropsWhere!] - type1Field: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - type1Field_EQ: Int - type1Field_GT: Int - type1Field_GTE: Int - type1Field_IN: [Int!] - type1Field_LT: Int - type1Field_LTE: Int + type1Field: IntScalarFilters + type1Field_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter type1Field: { eq: ... }\\") + type1Field_GT: Int @deprecated(reason: \\"Please use the relevant generic filter type1Field: { gt: ... }\\") + type1Field_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter type1Field: { gte: ... }\\") + type1Field_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter type1Field: { in: ... }\\") + type1Field_LT: Int @deprecated(reason: \\"Please use the relevant generic filter type1Field: { lt: ... }\\") + type1Field_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter type1Field: { lte: ... }\\") } \\"\\"\\" @@ -6983,8 +7701,8 @@ describe("Interface Relationships", () => { } input Type1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface1: [Type1Interface1UpdateFieldInput!] } @@ -6992,37 +7710,39 @@ describe("Interface Relationships", () => { AND: [Type1Where!] NOT: Type1Where OR: [Type1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface1: Interface1RelationshipFilters interface1Aggregate: Type1Interface1AggregateInput + interface1Connection: Type1Interface1ConnectionFilters \\"\\"\\" Return Type1s where all of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_ALL: Type1Interface1ConnectionWhere + interface1Connection_ALL: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where none of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_NONE: Type1Interface1ConnectionWhere + interface1Connection_NONE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where one of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SINGLE: Type1Interface1ConnectionWhere + interface1Connection_SINGLE: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type1s where some of the related Type1Interface1Connections match this filter \\"\\"\\" - interface1Connection_SOME: Type1Interface1ConnectionWhere + interface1Connection_SOME: Type1Interface1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface1Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Type1s where all of the related Interface1s match this filter\\"\\"\\" - interface1_ALL: Interface1Where + interface1_ALL: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { all: ... }' instead.\\") \\"\\"\\"Return Type1s where none of the related Interface1s match this filter\\"\\"\\" - interface1_NONE: Interface1Where + interface1_NONE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { none: ... }' instead.\\") \\"\\"\\"Return Type1s where one of the related Interface1s match this filter\\"\\"\\" - interface1_SINGLE: Interface1Where + interface1_SINGLE: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { single: ... }' instead.\\") \\"\\"\\"Return Type1s where some of the related Interface1s match this filter\\"\\"\\" - interface1_SOME: Interface1Where + interface1_SOME: Interface1Where @deprecated(reason: \\"Please use the relevant generic filter 'interface1: { some: ... }' instead.\\") } type Type1sConnection { @@ -7033,9 +7753,9 @@ describe("Interface Relationships", () => { type Type2Interface1 implements Interface1 { field1: String! - interface2(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: Interface2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! - interface2Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection - interface2Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! + interface2(limit: Int, offset: Int, sort: [Interface2Sort!], where: Interface2Where): [Interface2!]! + interface2Aggregate(where: Interface2Where): Type2Interface1Interface2Interface2AggregationSelection + interface2Connection(after: String, first: Int, sort: [Interface1Interface2ConnectionSort!], where: Interface1Interface2ConnectionWhere): Interface1Interface2Connection! } type Type2Interface1AggregateSelection { @@ -7061,7 +7781,7 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2AggregateInput!] NOT: Type2Interface1Interface2AggregateInput OR: [Type2Interface1Interface2AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7076,6 +7796,25 @@ describe("Interface Relationships", () => { where: Interface2ConnectWhere } + input Type2Interface1Interface2ConnectionFilters { + \\"\\"\\" + Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter + \\"\\"\\" + all: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter + \\"\\"\\" + none: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter + \\"\\"\\" + single: Interface1Interface2ConnectionWhere + \\"\\"\\" + Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter + \\"\\"\\" + some: Interface1Interface2ConnectionWhere + } + input Type2Interface1Interface2CreateFieldInput { edge: Type2PropsCreateInput! node: Interface2CreateInput! @@ -7112,21 +7851,22 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Interface2NodeAggregationWhereInput!] NOT: Type2Interface1Interface2NodeAggregationWhereInput OR: [Type2Interface1Interface2NodeAggregationWhereInput!] - field2_AVERAGE_LENGTH_EQUAL: Float - field2_AVERAGE_LENGTH_GT: Float - field2_AVERAGE_LENGTH_GTE: Float - field2_AVERAGE_LENGTH_LT: Float - field2_AVERAGE_LENGTH_LTE: Float - field2_LONGEST_LENGTH_EQUAL: Int - field2_LONGEST_LENGTH_GT: Int - field2_LONGEST_LENGTH_GTE: Int - field2_LONGEST_LENGTH_LT: Int - field2_LONGEST_LENGTH_LTE: Int - field2_SHORTEST_LENGTH_EQUAL: Int - field2_SHORTEST_LENGTH_GT: Int - field2_SHORTEST_LENGTH_GTE: Int - field2_SHORTEST_LENGTH_LT: Int - field2_SHORTEST_LENGTH_LTE: Int + field2: StringScalarAggregationFilters + field2_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { eq: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { gte: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lt: ... } } }' instead.\\") + field2_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'field2: { averageLength: { lte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { eq: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { gte: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lt: ... } } }' instead.\\") + field2_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { longestLength: { lte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { eq: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { gte: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lt: ... } } }' instead.\\") + field2_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'field2: { shortestLength: { lte: ... } } }' instead.\\") } input Type2Interface1Interface2UpdateConnectionInput { @@ -7143,15 +7883,6 @@ describe("Interface Relationships", () => { where: Interface1Interface2ConnectionWhere } - input Type2Interface1Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface1Sort objects to sort Type2Interface1s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface1Sort!] - } - \\"\\"\\" Fields to sort Type2Interface1s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface1Sort object. \\"\\"\\" @@ -7160,8 +7891,8 @@ describe("Interface Relationships", () => { } input Type2Interface1UpdateInput { - field1: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field1_SET: String + field1: StringScalarMutations + field1_SET: String @deprecated(reason: \\"Please use the generic mutation 'field1: { set: ... } }' instead.\\") interface2: [Type2Interface1Interface2UpdateFieldInput!] } @@ -7169,45 +7900,47 @@ describe("Interface Relationships", () => { AND: [Type2Interface1Where!] NOT: Type2Interface1Where OR: [Type2Interface1Where!] - field1: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field1_CONTAINS: String - field1_ENDS_WITH: String - field1_EQ: String - field1_IN: [String!] - field1_STARTS_WITH: String + field1: StringScalarFilters + field1_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field1: { contains: ... }\\") + field1_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { endsWith: ... }\\") + field1_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field1: { eq: ... }\\") + field1_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field1: { in: ... }\\") + field1_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field1: { startsWith: ... }\\") + interface2: Interface2RelationshipFilters interface2Aggregate: Type2Interface1Interface2AggregateInput + interface2Connection: Type2Interface1Interface2ConnectionFilters \\"\\"\\" Return Type2Interface1s where all of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_ALL: Interface1Interface2ConnectionWhere + interface2Connection_ALL: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_NONE: Interface1Interface2ConnectionWhere + interface2Connection_NONE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SINGLE: Interface1Interface2ConnectionWhere + interface2Connection_SINGLE: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface1Interface2Connections match this filter \\"\\"\\" - interface2Connection_SOME: Interface1Interface2ConnectionWhere + interface2Connection_SOME: Interface1Interface2ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'interface2Connection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return Type2Interface1s where all of the related Interface2s match this filter \\"\\"\\" - interface2_ALL: Interface2Where + interface2_ALL: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { all: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where none of the related Interface2s match this filter \\"\\"\\" - interface2_NONE: Interface2Where + interface2_NONE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { none: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where one of the related Interface2s match this filter \\"\\"\\" - interface2_SINGLE: Interface2Where + interface2_SINGLE: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { single: ... }' instead.\\") \\"\\"\\" Return Type2Interface1s where some of the related Interface2s match this filter \\"\\"\\" - interface2_SOME: Interface2Where + interface2_SOME: Interface2Where @deprecated(reason: \\"Please use the relevant generic filter 'interface2: { some: ... }' instead.\\") } type Type2Interface1sConnection { @@ -7234,15 +7967,6 @@ describe("Interface Relationships", () => { node: Type2Interface2! } - input Type2Interface2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Type2Interface2Sort objects to sort Type2Interface2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Type2Interface2Sort!] - } - \\"\\"\\" Fields to sort Type2Interface2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Type2Interface2Sort object. \\"\\"\\" @@ -7251,20 +7975,20 @@ describe("Interface Relationships", () => { } input Type2Interface2UpdateInput { - field2: String @deprecated(reason: \\"Please use the explicit _SET field\\") - field2_SET: String + field2: StringScalarMutations + field2_SET: String @deprecated(reason: \\"Please use the generic mutation 'field2: { set: ... } }' instead.\\") } input Type2Interface2Where { AND: [Type2Interface2Where!] NOT: Type2Interface2Where OR: [Type2Interface2Where!] - field2: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - field2_CONTAINS: String - field2_ENDS_WITH: String - field2_EQ: String - field2_IN: [String!] - field2_STARTS_WITH: String + field2: StringScalarFilters + field2_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter field2: { contains: ... }\\") + field2_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { endsWith: ... }\\") + field2_EQ: String @deprecated(reason: \\"Please use the relevant generic filter field2: { eq: ... }\\") + field2_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter field2: { in: ... }\\") + field2_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter field2: { startsWith: ... }\\") } type Type2Interface2sConnection { @@ -7285,26 +8009,27 @@ describe("Interface Relationships", () => { AND: [Type2PropsAggregationWhereInput!] NOT: Type2PropsAggregationWhereInput OR: [Type2PropsAggregationWhereInput!] - type2Field_AVERAGE_EQUAL: Float - type2Field_AVERAGE_GT: Float - type2Field_AVERAGE_GTE: Float - type2Field_AVERAGE_LT: Float - type2Field_AVERAGE_LTE: Float - type2Field_MAX_EQUAL: Int - type2Field_MAX_GT: Int - type2Field_MAX_GTE: Int - type2Field_MAX_LT: Int - type2Field_MAX_LTE: Int - type2Field_MIN_EQUAL: Int - type2Field_MIN_GT: Int - type2Field_MIN_GTE: Int - type2Field_MIN_LT: Int - type2Field_MIN_LTE: Int - type2Field_SUM_EQUAL: Int - type2Field_SUM_GT: Int - type2Field_SUM_GTE: Int - type2Field_SUM_LT: Int - type2Field_SUM_LTE: Int + type2Field: IntScalarAggregationFilters + type2Field_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { average: { eq: ... } } }' instead.\\") + type2Field_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { average: { gt: ... } } }' instead.\\") + type2Field_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { average: { gte: ... } } }' instead.\\") + type2Field_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { average: { lt: ... } } }' instead.\\") + type2Field_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { average: { lte: ... } } }' instead.\\") + type2Field_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { max: { eq: ... } } }' instead.\\") + type2Field_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { max: { gt: ... } } }' instead.\\") + type2Field_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { max: { gte: ... } } }' instead.\\") + type2Field_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { max: { lt: ... } } }' instead.\\") + type2Field_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { max: { lte: ... } } }' instead.\\") + type2Field_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { min: { eq: ... } } }' instead.\\") + type2Field_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { min: { gt: ... } } }' instead.\\") + type2Field_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { min: { gte: ... } } }' instead.\\") + type2Field_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { min: { lt: ... } } }' instead.\\") + type2Field_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { min: { lte: ... } } }' instead.\\") + type2Field_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { sum: { eq: ... } } }' instead.\\") + type2Field_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { sum: { gt: ... } } }' instead.\\") + type2Field_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { sum: { gte: ... } } }' instead.\\") + type2Field_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { sum: { lt: ... } } }' instead.\\") + type2Field_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'type2Field: { sum: { lte: ... } } }' instead.\\") } input Type2PropsCreateInput { @@ -7316,23 +8041,23 @@ describe("Interface Relationships", () => { } input Type2PropsUpdateInput { - type2Field: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - type2Field_DECREMENT: Int - type2Field_INCREMENT: Int - type2Field_SET: Int + type2Field: IntScalarMutations + type2Field_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'type2Field: { decrement: ... } }' instead.\\") + type2Field_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'type2Field: { increment: ... } }' instead.\\") + type2Field_SET: Int @deprecated(reason: \\"Please use the generic mutation 'type2Field: { set: ... } }' instead.\\") } input Type2PropsWhere { AND: [Type2PropsWhere!] NOT: Type2PropsWhere OR: [Type2PropsWhere!] - type2Field: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - type2Field_EQ: Int - type2Field_GT: Int - type2Field_GTE: Int - type2Field_IN: [Int!] - type2Field_LT: Int - type2Field_LTE: Int + type2Field: IntScalarFilters + type2Field_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter type2Field: { eq: ... }\\") + type2Field_GT: Int @deprecated(reason: \\"Please use the relevant generic filter type2Field: { gt: ... }\\") + type2Field_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter type2Field: { gte: ... }\\") + type2Field_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter type2Field: { in: ... }\\") + type2Field_LT: Int @deprecated(reason: \\"Please use the relevant generic filter type2Field: { lt: ... }\\") + type2Field_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter type2Field: { lte: ... }\\") } \\"\\"\\" @@ -7382,20 +8107,20 @@ describe("Interface Relationships", () => { interface Content { id: ID content: String - creator: User! @declareRelationship + creator: [User!]! @declareRelationship } type Comment implements Content @node { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) - post: Post! @relationship(type: "HAS_COMMENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) } type Post implements Content @node { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) comments: [Comment!]! @relationship(type: "HAS_COMMENT", direction: OUT) } @@ -7417,24 +8142,23 @@ describe("Interface Relationships", () => { type Comment implements Content { content: String - creator(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - creatorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): CommentUserCreatorAggregationSelection - creatorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ContentCreatorConnectionSort!], where: ContentCreatorConnectionWhere): ContentCreatorConnection! + creator(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + creatorAggregate(where: UserWhere): CommentUserCreatorAggregationSelection + creatorConnection(after: String, first: Int, sort: [ContentCreatorConnectionSort!], where: ContentCreatorConnectionWhere): ContentCreatorConnection! id: ID - post(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): Post! - postAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): CommentPostPostAggregationSelection - postConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [CommentPostConnectionSort!], where: CommentPostConnectionWhere): CommentPostConnection! + post(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postAggregate(where: PostWhere): CommentPostPostAggregationSelection + postConnection(after: String, first: Int, sort: [CommentPostConnectionSort!], where: CommentPostConnectionWhere): CommentPostConnection! } type CommentAggregateSelection { content: StringAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input CommentConnectInput { - creator: CommentCreatorConnectFieldInput - post: CommentPostConnectFieldInput + creator: [CommentCreatorConnectFieldInput!] + post: [CommentPostConnectFieldInput!] } input CommentConnectWhere { @@ -7452,7 +8176,7 @@ describe("Interface Relationships", () => { AND: [CommentCreatorAggregateInput!] NOT: CommentCreatorAggregateInput OR: [CommentCreatorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7462,12 +8186,27 @@ describe("Interface Relationships", () => { } input CommentCreatorConnectFieldInput { - connect: UserConnectInput + connect: [UserConnectInput!] + where: UserConnectWhere + } + + input CommentCreatorConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Comments where all of the related ContentCreatorConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere + all: ContentCreatorConnectionWhere + \\"\\"\\" + Return Comments where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + none: ContentCreatorConnectionWhere + \\"\\"\\" + Return Comments where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + single: ContentCreatorConnectionWhere + \\"\\"\\" + Return Comments where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + some: ContentCreatorConnectionWhere } input CommentCreatorCreateFieldInput { @@ -7475,39 +8214,30 @@ describe("Interface Relationships", () => { } input CommentCreatorFieldInput { - connect: CommentCreatorConnectFieldInput - create: CommentCreatorCreateFieldInput + connect: [CommentCreatorConnectFieldInput!] + create: [CommentCreatorCreateFieldInput!] } input CommentCreatorNodeAggregationWhereInput { AND: [CommentCreatorNodeAggregationWhereInput!] NOT: CommentCreatorNodeAggregationWhereInput OR: [CommentCreatorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input CommentCreatorUpdateConnectionInput { @@ -7515,22 +8245,22 @@ describe("Interface Relationships", () => { } input CommentCreatorUpdateFieldInput { - connect: CommentCreatorConnectFieldInput - create: CommentCreatorCreateFieldInput - delete: ContentCreatorDeleteFieldInput - disconnect: ContentCreatorDisconnectFieldInput + connect: [CommentCreatorConnectFieldInput!] + create: [CommentCreatorCreateFieldInput!] + delete: [ContentCreatorDeleteFieldInput!] + disconnect: [ContentCreatorDisconnectFieldInput!] update: CommentCreatorUpdateConnectionInput where: ContentCreatorConnectionWhere } input CommentDeleteInput { - creator: ContentCreatorDeleteFieldInput - post: CommentPostDeleteFieldInput + creator: [ContentCreatorDeleteFieldInput!] + post: [CommentPostDeleteFieldInput!] } input CommentDisconnectInput { - creator: ContentCreatorDisconnectFieldInput - post: CommentPostDisconnectFieldInput + creator: [ContentCreatorDisconnectFieldInput!] + post: [CommentPostDisconnectFieldInput!] } type CommentEdge { @@ -7538,20 +8268,11 @@ describe("Interface Relationships", () => { node: Comment! } - input CommentOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more CommentSort objects to sort Comments by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [CommentSort!] - } - input CommentPostAggregateInput { AND: [CommentPostAggregateInput!] NOT: CommentPostAggregateInput OR: [CommentPostAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7561,11 +8282,7 @@ describe("Interface Relationships", () => { } input CommentPostConnectFieldInput { - connect: PostConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [PostConnectInput!] where: PostConnectWhere } @@ -7575,6 +8292,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input CommentPostConnectionFilters { + \\"\\"\\" + Return Comments where all of the related CommentPostConnections match this filter + \\"\\"\\" + all: CommentPostConnectionWhere + \\"\\"\\" + Return Comments where none of the related CommentPostConnections match this filter + \\"\\"\\" + none: CommentPostConnectionWhere + \\"\\"\\" + Return Comments where one of the related CommentPostConnections match this filter + \\"\\"\\" + single: CommentPostConnectionWhere + \\"\\"\\" + Return Comments where some of the related CommentPostConnections match this filter + \\"\\"\\" + some: CommentPostConnectionWhere + } + input CommentPostConnectionSort { node: PostSort } @@ -7601,39 +8337,30 @@ describe("Interface Relationships", () => { } input CommentPostFieldInput { - connect: CommentPostConnectFieldInput - create: CommentPostCreateFieldInput + connect: [CommentPostConnectFieldInput!] + create: [CommentPostCreateFieldInput!] } input CommentPostNodeAggregationWhereInput { AND: [CommentPostNodeAggregationWhereInput!] NOT: CommentPostNodeAggregationWhereInput OR: [CommentPostNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + content: StringScalarAggregationFilters + content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { eq: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gte: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lte: ... } } }' instead.\\") + content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { eq: ... } } }' instead.\\") + content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gt: ... } } }' instead.\\") + content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gte: ... } } }' instead.\\") + content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lt: ... } } }' instead.\\") + content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { eq: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lte: ... } } }' instead.\\") } type CommentPostPostAggregationSelection { @@ -7643,7 +8370,6 @@ describe("Interface Relationships", () => { type CommentPostPostNodeAggregateSelection { content: StringAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } type CommentPostRelationship { @@ -7656,14 +8382,25 @@ describe("Interface Relationships", () => { } input CommentPostUpdateFieldInput { - connect: CommentPostConnectFieldInput - create: CommentPostCreateFieldInput - delete: CommentPostDeleteFieldInput - disconnect: CommentPostDisconnectFieldInput + connect: [CommentPostConnectFieldInput!] + create: [CommentPostCreateFieldInput!] + delete: [CommentPostDeleteFieldInput!] + disconnect: [CommentPostDisconnectFieldInput!] update: CommentPostUpdateConnectionInput where: CommentPostConnectionWhere } + input CommentRelationshipFilters { + \\"\\"\\"Filter type where all of the related Comments match this filter\\"\\"\\" + all: CommentWhere + \\"\\"\\"Filter type where none of the related Comments match this filter\\"\\"\\" + none: CommentWhere + \\"\\"\\"Filter type where one of the related Comments match this filter\\"\\"\\" + single: CommentWhere + \\"\\"\\"Filter type where some of the related Comments match this filter\\"\\"\\" + some: CommentWhere + } + \\"\\"\\" Fields to sort Comments by. The order in which sorts are applied is not guaranteed when specifying many fields in one CommentSort object. \\"\\"\\" @@ -7673,12 +8410,12 @@ describe("Interface Relationships", () => { } input CommentUpdateInput { - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String - creator: CommentCreatorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - post: CommentPostUpdateFieldInput + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") + creator: [CommentCreatorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + post: [CommentPostUpdateFieldInput!] } type CommentUserCreatorAggregationSelection { @@ -7687,7 +8424,6 @@ describe("Interface Relationships", () => { } type CommentUserCreatorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -7695,24 +8431,72 @@ describe("Interface Relationships", () => { AND: [CommentWhere!] NOT: CommentWhere OR: [CommentWhere!] - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String] - content_STARTS_WITH: String - creator: UserWhere + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") + creator: UserRelationshipFilters creatorAggregate: CommentCreatorAggregateInput - creatorConnection: ContentCreatorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - post: PostWhere + creatorConnection: CommentCreatorConnectionFilters + \\"\\"\\" + Return Comments where all of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_ALL: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_NONE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SINGLE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SOME: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Comments where all of the related Users match this filter\\"\\"\\" + creator_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { all: ... }' instead.\\") + \\"\\"\\"Return Comments where none of the related Users match this filter\\"\\"\\" + creator_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { none: ... }' instead.\\") + \\"\\"\\"Return Comments where one of the related Users match this filter\\"\\"\\" + creator_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { single: ... }' instead.\\") + \\"\\"\\"Return Comments where some of the related Users match this filter\\"\\"\\" + creator_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + post: PostRelationshipFilters postAggregate: CommentPostAggregateInput - postConnection: CommentPostConnectionWhere + postConnection: CommentPostConnectionFilters + \\"\\"\\" + Return Comments where all of the related CommentPostConnections match this filter + \\"\\"\\" + postConnection_ALL: CommentPostConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where none of the related CommentPostConnections match this filter + \\"\\"\\" + postConnection_NONE: CommentPostConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where one of the related CommentPostConnections match this filter + \\"\\"\\" + postConnection_SINGLE: CommentPostConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Comments where some of the related CommentPostConnections match this filter + \\"\\"\\" + postConnection_SOME: CommentPostConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'postConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Comments where all of the related Posts match this filter\\"\\"\\" + post_ALL: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'post: { all: ... }' instead.\\") + \\"\\"\\"Return Comments where none of the related Posts match this filter\\"\\"\\" + post_NONE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'post: { none: ... }' instead.\\") + \\"\\"\\"Return Comments where one of the related Posts match this filter\\"\\"\\" + post_SINGLE: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'post: { single: ... }' instead.\\") + \\"\\"\\"Return Comments where some of the related Posts match this filter\\"\\"\\" + post_SOME: PostWhere @deprecated(reason: \\"Please use the relevant generic filter 'post: { some: ... }' instead.\\") } type CommentsConnection { @@ -7723,7 +8507,7 @@ describe("Interface Relationships", () => { interface Content { content: String - creator(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! + creator(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! creatorConnection(after: String, first: Int, sort: [ContentCreatorConnectionSort!], where: ContentCreatorConnectionWhere): ContentCreatorConnection! id: ID } @@ -7731,11 +8515,10 @@ describe("Interface Relationships", () => { type ContentAggregateSelection { content: StringAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ContentConnectInput { - creator: ContentCreatorConnectFieldInput + creator: [ContentCreatorConnectFieldInput!] } input ContentConnectWhere { @@ -7751,7 +8534,7 @@ describe("Interface Relationships", () => { AND: [ContentCreatorAggregateInput!] NOT: ContentCreatorAggregateInput OR: [ContentCreatorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -7761,11 +8544,7 @@ describe("Interface Relationships", () => { } input ContentCreatorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [UserConnectInput!] where: UserConnectWhere } @@ -7775,6 +8554,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ContentCreatorConnectionFilters { + \\"\\"\\" + Return Contents where all of the related ContentCreatorConnections match this filter + \\"\\"\\" + all: ContentCreatorConnectionWhere + \\"\\"\\" + Return Contents where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + none: ContentCreatorConnectionWhere + \\"\\"\\" + Return Contents where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + single: ContentCreatorConnectionWhere + \\"\\"\\" + Return Contents where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + some: ContentCreatorConnectionWhere + } + input ContentCreatorConnectionSort { node: UserSort } @@ -7804,31 +8602,22 @@ describe("Interface Relationships", () => { AND: [ContentCreatorNodeAggregationWhereInput!] NOT: ContentCreatorNodeAggregationWhereInput OR: [ContentCreatorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ContentCreatorRelationship { @@ -7841,20 +8630,20 @@ describe("Interface Relationships", () => { } input ContentCreatorUpdateFieldInput { - connect: ContentCreatorConnectFieldInput - create: ContentCreatorCreateFieldInput - delete: ContentCreatorDeleteFieldInput - disconnect: ContentCreatorDisconnectFieldInput + connect: [ContentCreatorConnectFieldInput!] + create: [ContentCreatorCreateFieldInput!] + delete: [ContentCreatorDeleteFieldInput!] + disconnect: [ContentCreatorDisconnectFieldInput!] update: ContentCreatorUpdateConnectionInput where: ContentCreatorConnectionWhere } input ContentDeleteInput { - creator: ContentCreatorDeleteFieldInput + creator: [ContentCreatorDeleteFieldInput!] } input ContentDisconnectInput { - creator: ContentCreatorDisconnectFieldInput + creator: [ContentCreatorDisconnectFieldInput!] } type ContentEdge { @@ -7867,13 +8656,15 @@ describe("Interface Relationships", () => { Post } - input ContentOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ContentSort objects to sort Contents by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ContentSort!] + input ContentRelationshipFilters { + \\"\\"\\"Filter type where all of the related Contents match this filter\\"\\"\\" + all: ContentWhere + \\"\\"\\"Filter type where none of the related Contents match this filter\\"\\"\\" + none: ContentWhere + \\"\\"\\"Filter type where one of the related Contents match this filter\\"\\"\\" + single: ContentWhere + \\"\\"\\"Filter type where some of the related Contents match this filter\\"\\"\\" + some: ContentWhere } \\"\\"\\" @@ -7885,34 +8676,57 @@ describe("Interface Relationships", () => { } input ContentUpdateInput { - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String - creator: ContentCreatorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") + creator: [ContentCreatorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input ContentWhere { AND: [ContentWhere!] NOT: ContentWhere OR: [ContentWhere!] - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String] - content_STARTS_WITH: String - creator: UserWhere + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") + creator: UserRelationshipFilters creatorAggregate: ContentCreatorAggregateInput - creatorConnection: ContentCreatorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + creatorConnection: ContentCreatorConnectionFilters + \\"\\"\\" + Return Contents where all of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_ALL: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Contents where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_NONE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Contents where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SINGLE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Contents where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SOME: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Contents where all of the related Users match this filter\\"\\"\\" + creator_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { all: ... }' instead.\\") + \\"\\"\\"Return Contents where none of the related Users match this filter\\"\\"\\" + creator_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { none: ... }' instead.\\") + \\"\\"\\"Return Contents where one of the related Users match this filter\\"\\"\\" + creator_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { single: ... }' instead.\\") + \\"\\"\\"Return Contents where some of the related Users match this filter\\"\\"\\" + creator_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") typename: [ContentImplementation!] - typename_IN: [ContentImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ContentsConnection { @@ -7952,9 +8766,38 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Mutation { @@ -7978,20 +8821,19 @@ describe("Interface Relationships", () => { } type Post implements Content { - comments(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CommentOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CommentSort!], where: CommentWhere): [Comment!]! - commentsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CommentWhere): PostCommentCommentsAggregationSelection - commentsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostCommentsConnectionSort!], where: PostCommentsConnectionWhere): PostCommentsConnection! + comments(limit: Int, offset: Int, sort: [CommentSort!], where: CommentWhere): [Comment!]! + commentsAggregate(where: CommentWhere): PostCommentCommentsAggregationSelection + commentsConnection(after: String, first: Int, sort: [PostCommentsConnectionSort!], where: PostCommentsConnectionWhere): PostCommentsConnection! content: String - creator(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - creatorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserCreatorAggregationSelection - creatorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ContentCreatorConnectionSort!], where: ContentCreatorConnectionWhere): ContentCreatorConnection! + creator(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + creatorAggregate(where: UserWhere): PostUserCreatorAggregationSelection + creatorConnection(after: String, first: Int, sort: [ContentCreatorConnectionSort!], where: ContentCreatorConnectionWhere): ContentCreatorConnection! id: ID } type PostAggregateSelection { content: StringAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } type PostCommentCommentsAggregationSelection { @@ -8001,14 +8843,13 @@ describe("Interface Relationships", () => { type PostCommentCommentsNodeAggregateSelection { content: StringAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PostCommentsAggregateInput { AND: [PostCommentsAggregateInput!] NOT: PostCommentsAggregateInput OR: [PostCommentsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8019,10 +8860,6 @@ describe("Interface Relationships", () => { input PostCommentsConnectFieldInput { connect: [CommentConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: CommentConnectWhere } @@ -8032,6 +8869,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input PostCommentsConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostCommentsConnections match this filter + \\"\\"\\" + all: PostCommentsConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostCommentsConnections match this filter + \\"\\"\\" + none: PostCommentsConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostCommentsConnections match this filter + \\"\\"\\" + single: PostCommentsConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostCommentsConnections match this filter + \\"\\"\\" + some: PostCommentsConnectionWhere + } + input PostCommentsConnectionSort { node: CommentSort } @@ -8066,31 +8922,22 @@ describe("Interface Relationships", () => { AND: [PostCommentsNodeAggregationWhereInput!] NOT: PostCommentsNodeAggregationWhereInput OR: [PostCommentsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + content: StringScalarAggregationFilters + content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { eq: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gte: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lte: ... } } }' instead.\\") + content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { eq: ... } } }' instead.\\") + content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gt: ... } } }' instead.\\") + content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gte: ... } } }' instead.\\") + content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lt: ... } } }' instead.\\") + content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { eq: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lte: ... } } }' instead.\\") } type PostCommentsRelationship { @@ -8113,7 +8960,7 @@ describe("Interface Relationships", () => { input PostConnectInput { comments: [PostCommentsConnectFieldInput!] - creator: PostCreatorConnectFieldInput + creator: [PostCreatorConnectFieldInput!] } input PostConnectWhere { @@ -8131,7 +8978,7 @@ describe("Interface Relationships", () => { AND: [PostCreatorAggregateInput!] NOT: PostCreatorAggregateInput OR: [PostCreatorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8141,12 +8988,27 @@ describe("Interface Relationships", () => { } input PostCreatorConnectFieldInput { - connect: UserConnectInput + connect: [UserConnectInput!] + where: UserConnectWhere + } + + input PostCreatorConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Posts where all of the related ContentCreatorConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere + all: ContentCreatorConnectionWhere + \\"\\"\\" + Return Posts where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + none: ContentCreatorConnectionWhere + \\"\\"\\" + Return Posts where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + single: ContentCreatorConnectionWhere + \\"\\"\\" + Return Posts where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + some: ContentCreatorConnectionWhere } input PostCreatorCreateFieldInput { @@ -8154,39 +9016,30 @@ describe("Interface Relationships", () => { } input PostCreatorFieldInput { - connect: PostCreatorConnectFieldInput - create: PostCreatorCreateFieldInput + connect: [PostCreatorConnectFieldInput!] + create: [PostCreatorCreateFieldInput!] } input PostCreatorNodeAggregationWhereInput { AND: [PostCreatorNodeAggregationWhereInput!] NOT: PostCreatorNodeAggregationWhereInput OR: [PostCreatorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input PostCreatorUpdateConnectionInput { @@ -8194,22 +9047,22 @@ describe("Interface Relationships", () => { } input PostCreatorUpdateFieldInput { - connect: PostCreatorConnectFieldInput - create: PostCreatorCreateFieldInput - delete: ContentCreatorDeleteFieldInput - disconnect: ContentCreatorDisconnectFieldInput + connect: [PostCreatorConnectFieldInput!] + create: [PostCreatorCreateFieldInput!] + delete: [ContentCreatorDeleteFieldInput!] + disconnect: [ContentCreatorDisconnectFieldInput!] update: PostCreatorUpdateConnectionInput where: ContentCreatorConnectionWhere } input PostDeleteInput { comments: [PostCommentsDeleteFieldInput!] - creator: ContentCreatorDeleteFieldInput + creator: [ContentCreatorDeleteFieldInput!] } input PostDisconnectInput { comments: [PostCommentsDisconnectFieldInput!] - creator: ContentCreatorDisconnectFieldInput + creator: [ContentCreatorDisconnectFieldInput!] } type PostEdge { @@ -8217,13 +9070,15 @@ describe("Interface Relationships", () => { node: Post! } - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] + input PostRelationshipFilters { + \\"\\"\\"Filter type where all of the related Posts match this filter\\"\\"\\" + all: PostWhere + \\"\\"\\"Filter type where none of the related Posts match this filter\\"\\"\\" + none: PostWhere + \\"\\"\\"Filter type where one of the related Posts match this filter\\"\\"\\" + single: PostWhere + \\"\\"\\"Filter type where some of the related Posts match this filter\\"\\"\\" + some: PostWhere } \\"\\"\\" @@ -8236,11 +9091,11 @@ describe("Interface Relationships", () => { input PostUpdateInput { comments: [PostCommentsUpdateFieldInput!] - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String - creator: PostCreatorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + content: StringScalarMutations + content_SET: String @deprecated(reason: \\"Please use the generic mutation 'content: { set: ... } }' instead.\\") + creator: [PostCreatorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } type PostUserCreatorAggregationSelection { @@ -8249,7 +9104,6 @@ describe("Interface Relationships", () => { } type PostUserCreatorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -8257,46 +9111,72 @@ describe("Interface Relationships", () => { AND: [PostWhere!] NOT: PostWhere OR: [PostWhere!] + comments: CommentRelationshipFilters commentsAggregate: PostCommentsAggregateInput + commentsConnection: PostCommentsConnectionFilters \\"\\"\\" Return Posts where all of the related PostCommentsConnections match this filter \\"\\"\\" - commentsConnection_ALL: PostCommentsConnectionWhere + commentsConnection_ALL: PostCommentsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'commentsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where none of the related PostCommentsConnections match this filter \\"\\"\\" - commentsConnection_NONE: PostCommentsConnectionWhere + commentsConnection_NONE: PostCommentsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'commentsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where one of the related PostCommentsConnections match this filter \\"\\"\\" - commentsConnection_SINGLE: PostCommentsConnectionWhere + commentsConnection_SINGLE: PostCommentsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'commentsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Posts where some of the related PostCommentsConnections match this filter \\"\\"\\" - commentsConnection_SOME: PostCommentsConnectionWhere + commentsConnection_SOME: PostCommentsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'commentsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Posts where all of the related Comments match this filter\\"\\"\\" - comments_ALL: CommentWhere + comments_ALL: CommentWhere @deprecated(reason: \\"Please use the relevant generic filter 'comments: { all: ... }' instead.\\") \\"\\"\\"Return Posts where none of the related Comments match this filter\\"\\"\\" - comments_NONE: CommentWhere + comments_NONE: CommentWhere @deprecated(reason: \\"Please use the relevant generic filter 'comments: { none: ... }' instead.\\") \\"\\"\\"Return Posts where one of the related Comments match this filter\\"\\"\\" - comments_SINGLE: CommentWhere + comments_SINGLE: CommentWhere @deprecated(reason: \\"Please use the relevant generic filter 'comments: { single: ... }' instead.\\") \\"\\"\\"Return Posts where some of the related Comments match this filter\\"\\"\\" - comments_SOME: CommentWhere - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String] - content_STARTS_WITH: String - creator: UserWhere + comments_SOME: CommentWhere @deprecated(reason: \\"Please use the relevant generic filter 'comments: { some: ... }' instead.\\") + content: StringScalarFilters + content_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter content: { contains: ... }\\") + content_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { endsWith: ... }\\") + content_EQ: String @deprecated(reason: \\"Please use the relevant generic filter content: { eq: ... }\\") + content_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter content: { in: ... }\\") + content_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter content: { startsWith: ... }\\") + creator: UserRelationshipFilters creatorAggregate: PostCreatorAggregateInput - creatorConnection: ContentCreatorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + creatorConnection: PostCreatorConnectionFilters + \\"\\"\\" + Return Posts where all of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_ALL: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_NONE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SINGLE: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related ContentCreatorConnections match this filter + \\"\\"\\" + creatorConnection_SOME: ContentCreatorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'creatorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + creator_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + creator_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + creator_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + creator_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'creator: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type PostsConnection { @@ -8306,16 +9186,16 @@ describe("Interface Relationships", () => { } type Query { - comments(limit: Int, offset: Int, options: CommentOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CommentSort!], where: CommentWhere): [Comment!]! + comments(limit: Int, offset: Int, sort: [CommentSort!], where: CommentWhere): [Comment!]! commentsAggregate(where: CommentWhere): CommentAggregateSelection! commentsConnection(after: String, first: Int, sort: [CommentSort!], where: CommentWhere): CommentsConnection! - contents(limit: Int, offset: Int, options: ContentOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ContentSort!], where: ContentWhere): [Content!]! + contents(limit: Int, offset: Int, sort: [ContentSort!], where: ContentWhere): [Content!]! contentsAggregate(where: ContentWhere): ContentAggregateSelection! contentsConnection(after: String, first: Int, sort: [ContentSort!], where: ContentWhere): ContentsConnection! - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! postsAggregate(where: PostWhere): PostAggregateSelection! postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -8333,6 +9213,27 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateCommentsMutationResponse { comments: [Comment!]! info: UpdateInfo! @@ -8359,16 +9260,15 @@ describe("Interface Relationships", () => { } type User { - content(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ContentOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ContentSort!], where: ContentWhere): [Content!]! - contentAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ContentWhere): UserContentContentAggregationSelection - contentConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserContentConnectionSort!], where: UserContentConnectionWhere): UserContentConnection! + content(limit: Int, offset: Int, sort: [ContentSort!], where: ContentWhere): [Content!]! + contentAggregate(where: ContentWhere): UserContentContentAggregationSelection + contentConnection(after: String, first: Int, sort: [UserContentConnectionSort!], where: UserContentConnectionWhere): UserContentConnection! id: ID name: String } type UserAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -8384,7 +9284,7 @@ describe("Interface Relationships", () => { AND: [UserContentAggregateInput!] NOT: UserContentAggregateInput OR: [UserContentAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8404,6 +9304,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input UserContentConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserContentConnections match this filter + \\"\\"\\" + all: UserContentConnectionWhere + \\"\\"\\" + Return Users where none of the related UserContentConnections match this filter + \\"\\"\\" + none: UserContentConnectionWhere + \\"\\"\\" + Return Users where one of the related UserContentConnections match this filter + \\"\\"\\" + single: UserContentConnectionWhere + \\"\\"\\" + Return Users where some of the related UserContentConnections match this filter + \\"\\"\\" + some: UserContentConnectionWhere + } + input UserContentConnectionSort { node: ContentSort } @@ -8422,7 +9341,6 @@ describe("Interface Relationships", () => { type UserContentContentNodeAggregateSelection { content: StringAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input UserContentCreateFieldInput { @@ -8448,31 +9366,22 @@ describe("Interface Relationships", () => { AND: [UserContentNodeAggregationWhereInput!] NOT: UserContentNodeAggregationWhereInput OR: [UserContentNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + content: StringScalarAggregationFilters + content_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { eq: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { gte: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lt: ... } } }' instead.\\") + content_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'content: { averageLength: { lte: ... } } }' instead.\\") + content_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { eq: ... } } }' instead.\\") + content_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gt: ... } } }' instead.\\") + content_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { gte: ... } } }' instead.\\") + content_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lt: ... } } }' instead.\\") + content_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { longestLength: { lte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { eq: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { gte: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lt: ... } } }' instead.\\") + content_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'content: { shortestLength: { lte: ... } } }' instead.\\") } type UserContentRelationship { @@ -8512,13 +9421,15 @@ describe("Interface Relationships", () => { node: User! } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere } \\"\\"\\" @@ -8531,53 +9442,55 @@ describe("Interface Relationships", () => { input UserUpdateInput { content: [UserContentUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] + content: ContentRelationshipFilters contentAggregate: UserContentAggregateInput + contentConnection: UserContentConnectionFilters \\"\\"\\" Return Users where all of the related UserContentConnections match this filter \\"\\"\\" - contentConnection_ALL: UserContentConnectionWhere + contentConnection_ALL: UserContentConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contentConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserContentConnections match this filter \\"\\"\\" - contentConnection_NONE: UserContentConnectionWhere + contentConnection_NONE: UserContentConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contentConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserContentConnections match this filter \\"\\"\\" - contentConnection_SINGLE: UserContentConnectionWhere + contentConnection_SINGLE: UserContentConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contentConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserContentConnections match this filter \\"\\"\\" - contentConnection_SOME: UserContentConnectionWhere + contentConnection_SOME: UserContentConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contentConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Contents match this filter\\"\\"\\" - content_ALL: ContentWhere + content_ALL: ContentWhere @deprecated(reason: \\"Please use the relevant generic filter 'content: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Contents match this filter\\"\\"\\" - content_NONE: ContentWhere + content_NONE: ContentWhere @deprecated(reason: \\"Please use the relevant generic filter 'content: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Contents match this filter\\"\\"\\" - content_SINGLE: ContentWhere + content_SINGLE: ContentWhere @deprecated(reason: \\"Please use the relevant generic filter 'content: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Contents match this filter\\"\\"\\" - content_SOME: ContentWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + content_SOME: ContentWhere @deprecated(reason: \\"Please use the relevant generic filter 'content: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type UsersConnection { @@ -8648,26 +9561,27 @@ describe("Interface Relationships", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -8679,29 +9593,29 @@ describe("Interface Relationships", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ShowOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ShowSort!], where: ShowWhere): [Show!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ShowWhere): ActorShowActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ShowSort!], where: ShowWhere): [Show!]! + actedInAggregate(where: ShowWhere): ActorShowActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -8709,7 +9623,7 @@ describe("Interface Relationships", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8731,6 +9645,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ShowSort @@ -8768,21 +9701,22 @@ describe("Interface Relationships", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -8836,13 +9770,15 @@ describe("Interface Relationships", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } type ActorShowActedInAggregationSelection { @@ -8868,45 +9804,47 @@ describe("Interface Relationships", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ShowRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Shows match this filter\\"\\"\\" - actedIn_ALL: ShowWhere + actedIn_ALL: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Shows match this filter\\"\\"\\" - actedIn_NONE: ShowWhere + actedIn_NONE: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Shows match this filter\\"\\"\\" - actedIn_SINGLE: ShowWhere + actedIn_SINGLE: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Shows match this filter\\"\\"\\" - actedIn_SOME: ShowWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -8946,6 +9884,16 @@ describe("Interface Relationships", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -8953,10 +9901,35 @@ describe("Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production & Show { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! runtime: Int! title: String! } @@ -8979,7 +9952,7 @@ describe("Interface Relationships", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -8992,11 +9965,26 @@ describe("Interface Relationships", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ShowActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -9013,21 +10001,22 @@ describe("Interface Relationships", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -9065,15 +10054,6 @@ describe("Interface Relationships", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -9084,56 +10064,58 @@ describe("Interface Relationships", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -9182,15 +10164,6 @@ describe("Interface Relationships", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] - } - \\"\\"\\" Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. \\"\\"\\" @@ -9202,14 +10175,13 @@ describe("Interface Relationships", () => { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -9219,27 +10191,27 @@ describe("Interface Relationships", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - shows(limit: Int, offset: Int, options: ShowOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ShowSort!], where: ShowWhere): [Show!]! + shows(limit: Int, offset: Int, sort: [ShowSort!], where: ShowWhere): [Show!]! showsAggregate(where: ShowWhere): ShowAggregateSelection! showsConnection(after: String, first: Int, sort: [ShowSort!], where: ShowWhere): ShowsConnection! } type Series implements Production & Show { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! episodeCount: Int! title: String! } @@ -9262,7 +10234,7 @@ describe("Interface Relationships", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9275,11 +10247,26 @@ describe("Interface Relationships", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: StarredInCreateInput! + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Series where all of the related ShowActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -9296,21 +10283,22 @@ describe("Interface Relationships", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -9354,15 +10342,6 @@ describe("Interface Relationships", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -9373,60 +10352,62 @@ describe("Interface Relationships", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodeCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodeCount_DECREMENT: Int - episodeCount_INCREMENT: Int - episodeCount_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodeCount: IntScalarMutations + episodeCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { decrement: ... } }' instead.\\") + episodeCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeCount: { increment: ... } }' instead.\\") + episodeCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodeCount: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodeCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodeCount_EQ: Int - episodeCount_GT: Int - episodeCount_GTE: Int - episodeCount_IN: [Int!] - episodeCount_LT: Int - episodeCount_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodeCount: IntScalarFilters + episodeCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { eq: ... }\\") + episodeCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gt: ... }\\") + episodeCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { gte: ... }\\") + episodeCount_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { in: ... }\\") + episodeCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lt: ... }\\") + episodeCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeCount: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } interface Show { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! title: String! } @@ -9435,7 +10416,7 @@ describe("Interface Relationships", () => { AND: [ShowActorsAggregateInput!] NOT: ShowActorsAggregateInput OR: [ShowActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -9448,10 +10429,6 @@ describe("Interface Relationships", () => { input ShowActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ShowActorsEdgeCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -9461,6 +10438,25 @@ describe("Interface Relationships", () => { totalCount: Int! } + input ShowActorsConnectionFilters { + \\"\\"\\" + Return Shows where all of the related ShowActorsConnections match this filter + \\"\\"\\" + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere + } + input ShowActorsConnectionSort { edge: ShowActorsEdgeSort node: ActorSort @@ -9558,21 +10554,22 @@ describe("Interface Relationships", () => { AND: [ShowActorsNodeAggregationWhereInput!] NOT: ShowActorsNodeAggregationWhereInput OR: [ShowActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ShowActorsRelationship { @@ -9633,13 +10630,15 @@ describe("Interface Relationships", () => { Series } - input ShowOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ShowSort objects to sort Shows by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ShowSort!] + input ShowRelationshipFilters { + \\"\\"\\"Filter type where all of the related Shows match this filter\\"\\"\\" + all: ShowWhere + \\"\\"\\"Filter type where none of the related Shows match this filter\\"\\"\\" + none: ShowWhere + \\"\\"\\"Filter type where one of the related Shows match this filter\\"\\"\\" + single: ShowWhere + \\"\\"\\"Filter type where some of the related Shows match this filter\\"\\"\\" + some: ShowWhere } \\"\\"\\" @@ -9651,47 +10650,48 @@ describe("Interface Relationships", () => { input ShowUpdateInput { actors: [ShowActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ShowWhere { AND: [ShowWhere!] NOT: ShowWhere OR: [ShowWhere!] + actors: ActorRelationshipFilters actorsAggregate: ShowActorsAggregateInput + actorsConnection: ShowActorsConnectionFilters \\"\\"\\" Return Shows where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Shows where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Shows where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Shows where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Shows where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ShowImplementation!] - typename_IN: [ShowImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ShowsConnection { @@ -9720,26 +10720,27 @@ describe("Interface Relationships", () => { AND: [StarredInAggregationWhereInput!] NOT: StarredInAggregationWhereInput OR: [StarredInAggregationWhereInput!] - episodeNr_AVERAGE_EQUAL: Float - episodeNr_AVERAGE_GT: Float - episodeNr_AVERAGE_GTE: Float - episodeNr_AVERAGE_LT: Float - episodeNr_AVERAGE_LTE: Float - episodeNr_MAX_EQUAL: Int - episodeNr_MAX_GT: Int - episodeNr_MAX_GTE: Int - episodeNr_MAX_LT: Int - episodeNr_MAX_LTE: Int - episodeNr_MIN_EQUAL: Int - episodeNr_MIN_GT: Int - episodeNr_MIN_GTE: Int - episodeNr_MIN_LT: Int - episodeNr_MIN_LTE: Int - episodeNr_SUM_EQUAL: Int - episodeNr_SUM_GT: Int - episodeNr_SUM_GTE: Int - episodeNr_SUM_LT: Int - episodeNr_SUM_LTE: Int + episodeNr: IntScalarAggregationFilters + episodeNr_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { average: { eq: ... } } }' instead.\\") + episodeNr_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { average: { gt: ... } } }' instead.\\") + episodeNr_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { average: { gte: ... } } }' instead.\\") + episodeNr_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { average: { lt: ... } } }' instead.\\") + episodeNr_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { average: { lte: ... } } }' instead.\\") + episodeNr_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { max: { eq: ... } } }' instead.\\") + episodeNr_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { max: { gt: ... } } }' instead.\\") + episodeNr_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { max: { gte: ... } } }' instead.\\") + episodeNr_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { max: { lt: ... } } }' instead.\\") + episodeNr_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { max: { lte: ... } } }' instead.\\") + episodeNr_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { min: { eq: ... } } }' instead.\\") + episodeNr_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { min: { gt: ... } } }' instead.\\") + episodeNr_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { min: { gte: ... } } }' instead.\\") + episodeNr_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { min: { lt: ... } } }' instead.\\") + episodeNr_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { min: { lte: ... } } }' instead.\\") + episodeNr_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { sum: { eq: ... } } }' instead.\\") + episodeNr_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { sum: { gt: ... } } }' instead.\\") + episodeNr_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { sum: { gte: ... } } }' instead.\\") + episodeNr_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { sum: { lt: ... } } }' instead.\\") + episodeNr_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'episodeNr: { sum: { lte: ... } } }' instead.\\") } input StarredInCreateInput { @@ -9751,23 +10752,23 @@ describe("Interface Relationships", () => { } input StarredInUpdateInput { - episodeNr: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodeNr_DECREMENT: Int - episodeNr_INCREMENT: Int - episodeNr_SET: Int + episodeNr: IntScalarMutations + episodeNr_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeNr: { decrement: ... } }' instead.\\") + episodeNr_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodeNr: { increment: ... } }' instead.\\") + episodeNr_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodeNr: { set: ... } }' instead.\\") } input StarredInWhere { AND: [StarredInWhere!] NOT: StarredInWhere OR: [StarredInWhere!] - episodeNr: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodeNr_EQ: Int - episodeNr_GT: Int - episodeNr_GTE: Int - episodeNr_IN: [Int!] - episodeNr_LT: Int - episodeNr_LTE: Int + episodeNr: IntScalarFilters + episodeNr_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { eq: ... }\\") + episodeNr_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { gt: ... }\\") + episodeNr_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { gte: ... }\\") + episodeNr_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { in: ... }\\") + episodeNr_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { lt: ... }\\") + episodeNr_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodeNr: { lte: ... }\\") } type StringAggregateSelection { @@ -9775,6 +10776,27 @@ describe("Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/interfaces.test.ts b/packages/graphql/tests/schema/interfaces.test.ts index cc7a924385..2676b68ea1 100644 --- a/packages/graphql/tests/schema/interfaces.test.ts +++ b/packages/graphql/tests/schema/interfaces.test.ts @@ -33,7 +33,7 @@ describe("Interfaces", () => { type Movie implements MovieNode @node { id: ID - nodes: [MovieNode] + nodes: [MovieNode!] movies: [Movie!]! @relationship(type: "HAS_MOVIE", direction: OUT) customQuery: [Movie] @cypher( @@ -75,23 +75,41 @@ describe("Interfaces", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie implements MovieNode { customQuery: [Movie] id: ID - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): MovieMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! - nodes: [MovieNode] + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! + nodes: [MovieNode!] } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -122,33 +140,42 @@ describe("Interfaces", () => { type MovieMovieMoviesAggregationSelection { count: Int! - node: MovieMovieMoviesNodeAggregateSelection - } - - type MovieMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieMoviesAggregateInput { AND: [MovieMoviesAggregateInput!] NOT: MovieMoviesAggregateInput OR: [MovieMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: MovieMoviesNodeAggregationWhereInput } input MovieMoviesConnectFieldInput { connect: [MovieConnectInput!] + where: MovieConnectWhere + } + + input MovieMoviesConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere + all: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + none: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + single: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + some: MovieNodeMoviesConnectionWhere } input MovieMoviesCreateFieldInput { @@ -160,22 +187,6 @@ describe("Interfaces", () => { create: [MovieMoviesCreateFieldInput!] } - input MovieMoviesNodeAggregationWhereInput { - AND: [MovieMoviesNodeAggregationWhereInput!] - NOT: MovieMoviesNodeAggregationWhereInput - OR: [MovieMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - input MovieMoviesUpdateConnectionInput { node: MovieUpdateInput } @@ -192,13 +203,12 @@ describe("Interfaces", () => { interface MovieNode { customQuery: [Movie] id: ID - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! } type MovieNodeAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } type MovieNodeEdge { @@ -214,13 +224,12 @@ describe("Interfaces", () => { AND: [MovieNodeMoviesAggregateInput!] NOT: MovieNodeMoviesAggregateInput OR: [MovieNodeMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: MovieNodeMoviesNodeAggregationWhereInput } type MovieNodeMoviesConnection { @@ -229,6 +238,25 @@ describe("Interfaces", () => { totalCount: Int! } + input MovieNodeMoviesConnectionFilters { + \\"\\"\\" + Return MovieNodes where all of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + all: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where none of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + none: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where one of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + single: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where some of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + some: MovieNodeMoviesConnectionWhere + } + input MovieNodeMoviesConnectionSort { node: MovieSort } @@ -250,36 +278,11 @@ describe("Interfaces", () => { where: MovieNodeMoviesConnectionWhere } - input MovieNodeMoviesNodeAggregationWhereInput { - AND: [MovieNodeMoviesNodeAggregationWhereInput!] - NOT: MovieNodeMoviesNodeAggregationWhereInput - OR: [MovieNodeMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type MovieNodeMoviesRelationship { cursor: String! node: Movie! } - input MovieNodeOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieNodeSort objects to sort MovieNodes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieNodeSort!] - } - \\"\\"\\" Fields to sort MovieNodes by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieNodeSort object. \\"\\"\\" @@ -291,39 +294,40 @@ describe("Interfaces", () => { AND: [MovieNodeWhere!] NOT: MovieNodeWhere OR: [MovieNodeWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: MovieNodeMoviesAggregateInput + moviesConnection: MovieNodeMoviesConnectionFilters \\"\\"\\" Return MovieNodes where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: MovieNodeMoviesConnectionWhere + moviesConnection_ALL: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where none of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: MovieNodeMoviesConnectionWhere + moviesConnection_NONE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where one of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere + moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where some of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: MovieNodeMoviesConnectionWhere + moviesConnection_SOME: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return MovieNodes where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return MovieNodes where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return MovieNodes where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return MovieNodes where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") typename: [MovieNodeImplementation!] - typename_IN: [MovieNodeImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type MovieNodesConnection { @@ -332,13 +336,15 @@ describe("Interfaces", () => { totalCount: Int! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -349,8 +355,8 @@ describe("Interfaces", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") movies: [MovieMoviesUpdateFieldInput!] } @@ -358,42 +364,44 @@ describe("Interfaces", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - customQuery: MovieWhere + customQuery: MovieRelationshipFilters customQuery_ALL: MovieWhere customQuery_NONE: MovieWhere customQuery_SINGLE: MovieWhere customQuery_SOME: MovieWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: MovieMoviesAggregateInput + moviesConnection: MovieMoviesConnectionFilters \\"\\"\\" Return Movies where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: MovieNodeMoviesConnectionWhere + moviesConnection_ALL: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: MovieNodeMoviesConnectionWhere + moviesConnection_NONE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere + moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: MovieNodeMoviesConnectionWhere + moviesConnection_SOME: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type MoviesConnection { @@ -417,10 +425,10 @@ describe("Interfaces", () => { } type Query { - movieNodes(limit: Int, offset: Int, options: MovieNodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieNodeSort!], where: MovieNodeWhere): [MovieNode!]! + movieNodes(limit: Int, offset: Int, sort: [MovieNodeSort!], where: MovieNodeWhere): [MovieNode!]! movieNodesAggregate(where: MovieNodeWhere): MovieNodeAggregateSelection! movieNodesConnection(after: String, first: Int, sort: [MovieNodeSort!], where: MovieNodeWhere): MovieNodesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -461,7 +469,7 @@ describe("Interfaces", () => { type Movie implements MovieNode @node { id: ID - nodes: [MovieNode] + nodes: [MovieNode!] movies: [Movie!]! @relationship(type: "HAS_MOVIE", direction: OUT) customQuery: [Movie] @cypher( @@ -505,23 +513,41 @@ describe("Interfaces", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie implements MovieNode { customQuery: [Movie] id: ID - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): MovieMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! - nodes: [MovieNode] + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! + nodes: [MovieNode!] } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -552,33 +578,42 @@ describe("Interfaces", () => { type MovieMovieMoviesAggregationSelection { count: Int! - node: MovieMovieMoviesNodeAggregateSelection - } - - type MovieMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieMoviesAggregateInput { AND: [MovieMoviesAggregateInput!] NOT: MovieMoviesAggregateInput OR: [MovieMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: MovieMoviesNodeAggregationWhereInput } input MovieMoviesConnectFieldInput { connect: [MovieConnectInput!] + where: MovieConnectWhere + } + + input MovieMoviesConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere + all: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + none: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + single: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + some: MovieNodeMoviesConnectionWhere } input MovieMoviesCreateFieldInput { @@ -590,22 +625,6 @@ describe("Interfaces", () => { create: [MovieMoviesCreateFieldInput!] } - input MovieMoviesNodeAggregationWhereInput { - AND: [MovieMoviesNodeAggregationWhereInput!] - NOT: MovieMoviesNodeAggregationWhereInput - OR: [MovieMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - input MovieMoviesUpdateConnectionInput { node: MovieUpdateInput } @@ -622,13 +641,12 @@ describe("Interfaces", () => { interface MovieNode @something(something: \\"test\\") { customQuery: [Movie] id: ID - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesConnection(after: String, first: Int, sort: [MovieNodeMoviesConnectionSort!], where: MovieNodeMoviesConnectionWhere): MovieNodeMoviesConnection! } type MovieNodeAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } type MovieNodeEdge { @@ -644,13 +662,12 @@ describe("Interfaces", () => { AND: [MovieNodeMoviesAggregateInput!] NOT: MovieNodeMoviesAggregateInput OR: [MovieNodeMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: MovieNodeMoviesNodeAggregationWhereInput } type MovieNodeMoviesConnection { @@ -659,6 +676,25 @@ describe("Interfaces", () => { totalCount: Int! } + input MovieNodeMoviesConnectionFilters { + \\"\\"\\" + Return MovieNodes where all of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + all: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where none of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + none: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where one of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + single: MovieNodeMoviesConnectionWhere + \\"\\"\\" + Return MovieNodes where some of the related MovieNodeMoviesConnections match this filter + \\"\\"\\" + some: MovieNodeMoviesConnectionWhere + } + input MovieNodeMoviesConnectionSort { node: MovieSort } @@ -680,36 +716,11 @@ describe("Interfaces", () => { where: MovieNodeMoviesConnectionWhere } - input MovieNodeMoviesNodeAggregationWhereInput { - AND: [MovieNodeMoviesNodeAggregationWhereInput!] - NOT: MovieNodeMoviesNodeAggregationWhereInput - OR: [MovieNodeMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type MovieNodeMoviesRelationship { cursor: String! node: Movie! } - input MovieNodeOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieNodeSort objects to sort MovieNodes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieNodeSort!] - } - \\"\\"\\" Fields to sort MovieNodes by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieNodeSort object. \\"\\"\\" @@ -721,39 +732,40 @@ describe("Interfaces", () => { AND: [MovieNodeWhere!] NOT: MovieNodeWhere OR: [MovieNodeWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: MovieNodeMoviesAggregateInput + moviesConnection: MovieNodeMoviesConnectionFilters \\"\\"\\" Return MovieNodes where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: MovieNodeMoviesConnectionWhere + moviesConnection_ALL: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where none of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: MovieNodeMoviesConnectionWhere + moviesConnection_NONE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where one of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere + moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return MovieNodes where some of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: MovieNodeMoviesConnectionWhere + moviesConnection_SOME: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return MovieNodes where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return MovieNodes where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return MovieNodes where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return MovieNodes where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") typename: [MovieNodeImplementation!] - typename_IN: [MovieNodeImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type MovieNodesConnection { @@ -762,13 +774,15 @@ describe("Interfaces", () => { totalCount: Int! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -779,8 +793,8 @@ describe("Interfaces", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") movies: [MovieMoviesUpdateFieldInput!] } @@ -788,42 +802,44 @@ describe("Interfaces", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - customQuery: MovieWhere + customQuery: MovieRelationshipFilters customQuery_ALL: MovieWhere customQuery_NONE: MovieWhere customQuery_SINGLE: MovieWhere customQuery_SOME: MovieWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: MovieMoviesAggregateInput + moviesConnection: MovieMoviesConnectionFilters \\"\\"\\" Return Movies where all of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: MovieNodeMoviesConnectionWhere + moviesConnection_ALL: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: MovieNodeMoviesConnectionWhere + moviesConnection_NONE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere + moviesConnection_SINGLE: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieNodeMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: MovieNodeMoviesConnectionWhere + moviesConnection_SOME: MovieNodeMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type MoviesConnection { @@ -847,10 +863,10 @@ describe("Interfaces", () => { } type Query { - movieNodes(limit: Int, offset: Int, options: MovieNodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieNodeSort!], where: MovieNodeWhere): [MovieNode!]! + movieNodes(limit: Int, offset: Int, sort: [MovieNodeSort!], where: MovieNodeWhere): [MovieNode!]! movieNodesAggregate(where: MovieNodeWhere): MovieNodeAggregateSelection! movieNodesConnection(after: String, first: Int, sort: [MovieNodeSort!], where: MovieNodeWhere): MovieNodesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/issues/1038.test.ts b/packages/graphql/tests/schema/issues/1038.test.ts index dd3a671e10..1142dd44e1 100644 --- a/packages/graphql/tests/schema/issues/1038.test.ts +++ b/packages/graphql/tests/schema/issues/1038.test.ts @@ -65,15 +65,6 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { node: AWSAccount! } - input AWSAccountOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AWSAccountSort objects to sort AwsAccounts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AWSAccountSort!] - } - \\"\\"\\" Fields to sort AwsAccounts by. The order in which sorts are applied is not guaranteed when specifying many fields in one AWSAccountSort object. \\"\\"\\" @@ -83,28 +74,28 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { } input AWSAccountUpdateInput { - accountName: String @deprecated(reason: \\"Please use the explicit _SET field\\") - accountName_SET: String - code: String @deprecated(reason: \\"Please use the explicit _SET field\\") - code_SET: String + accountName: StringScalarMutations + accountName_SET: String @deprecated(reason: \\"Please use the generic mutation 'accountName: { set: ... } }' instead.\\") + code: StringScalarMutations + code_SET: String @deprecated(reason: \\"Please use the generic mutation 'code: { set: ... } }' instead.\\") } input AWSAccountWhere { AND: [AWSAccountWhere!] NOT: AWSAccountWhere OR: [AWSAccountWhere!] - accountName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - accountName_CONTAINS: String - accountName_ENDS_WITH: String - accountName_EQ: String - accountName_IN: [String] - accountName_STARTS_WITH: String - code: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - code_CONTAINS: String - code_ENDS_WITH: String - code_EQ: String - code_IN: [String] - code_STARTS_WITH: String + accountName: StringScalarFilters + accountName_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter accountName: { contains: ... }\\") + accountName_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter accountName: { endsWith: ... }\\") + accountName_EQ: String @deprecated(reason: \\"Please use the relevant generic filter accountName: { eq: ... }\\") + accountName_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter accountName: { in: ... }\\") + accountName_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter accountName: { startsWith: ... }\\") + code: StringScalarFilters + code_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter code: { contains: ... }\\") + code_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter code: { endsWith: ... }\\") + code_EQ: String @deprecated(reason: \\"Please use the relevant generic filter code: { eq: ... }\\") + code_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter code: { in: ... }\\") + code_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter code: { startsWith: ... }\\") } type AwsAccountsConnection { @@ -152,15 +143,6 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { node: DNSZone! } - input DNSZoneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more DNSZoneSort objects to sort DnsZones by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [DNSZoneSort!] - } - \\"\\"\\" Fields to sort DnsZones by. The order in which sorts are applied is not guaranteed when specifying many fields in one DNSZoneSort object. \\"\\"\\" @@ -170,28 +152,28 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { } input DNSZoneUpdateInput { - awsId: String @deprecated(reason: \\"Please use the explicit _SET field\\") - awsId_SET: String - zoneType: String @deprecated(reason: \\"Please use the explicit _SET field\\") - zoneType_SET: String + awsId: StringScalarMutations + awsId_SET: String @deprecated(reason: \\"Please use the generic mutation 'awsId: { set: ... } }' instead.\\") + zoneType: StringScalarMutations + zoneType_SET: String @deprecated(reason: \\"Please use the generic mutation 'zoneType: { set: ... } }' instead.\\") } input DNSZoneWhere { AND: [DNSZoneWhere!] NOT: DNSZoneWhere OR: [DNSZoneWhere!] - awsId: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - awsId_CONTAINS: String - awsId_ENDS_WITH: String - awsId_EQ: String - awsId_IN: [String] - awsId_STARTS_WITH: String - zoneType: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - zoneType_CONTAINS: String - zoneType_ENDS_WITH: String - zoneType_EQ: String - zoneType_IN: [String] - zoneType_STARTS_WITH: String + awsId: StringScalarFilters + awsId_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter awsId: { contains: ... }\\") + awsId_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter awsId: { endsWith: ... }\\") + awsId_EQ: String @deprecated(reason: \\"Please use the relevant generic filter awsId: { eq: ... }\\") + awsId_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter awsId: { in: ... }\\") + awsId_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter awsId: { startsWith: ... }\\") + zoneType: StringScalarFilters + zoneType_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter zoneType: { contains: ... }\\") + zoneType_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter zoneType: { endsWith: ... }\\") + zoneType_EQ: String @deprecated(reason: \\"Please use the relevant generic filter zoneType: { eq: ... }\\") + zoneType_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter zoneType: { in: ... }\\") + zoneType_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter zoneType: { startsWith: ... }\\") } \\"\\"\\" @@ -226,10 +208,10 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { } type Query { - awsAccounts(limit: Int, offset: Int, options: AWSAccountOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AWSAccountSort!], where: AWSAccountWhere): [AWSAccount!]! + awsAccounts(limit: Int, offset: Int, sort: [AWSAccountSort!], where: AWSAccountWhere): [AWSAccount!]! awsAccountsAggregate(where: AWSAccountWhere): AWSAccountAggregateSelection! awsAccountsConnection(after: String, first: Int, sort: [AWSAccountSort!], where: AWSAccountWhere): AwsAccountsConnection! - dnsZones(limit: Int, offset: Int, options: DNSZoneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [DNSZoneSort!], where: DNSZoneWhere): [DNSZone!]! + dnsZones(limit: Int, offset: Int, sort: [DNSZoneSort!], where: DNSZoneWhere): [DNSZone!]! dnsZonesAggregate(where: DNSZoneWhere): DNSZoneAggregateSelection! dnsZonesConnection(after: String, first: Int, sort: [DNSZoneSort!], where: DNSZoneWhere): DnsZonesConnection! } @@ -247,6 +229,20 @@ describe("https://github.com/neo4j/graphql/issues/1038", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateAwsAccountsMutationResponse { awsAccounts: [AWSAccount!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/1182.test.ts b/packages/graphql/tests/schema/issues/1182.test.ts index 16dc6b1296..4cb219ee7b 100644 --- a/packages/graphql/tests/schema/issues/1182.test.ts +++ b/packages/graphql/tests/schema/issues/1182.test.ts @@ -26,13 +26,13 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { test("DateTime and Point fields are included in onCreate", async () => { const typeDefs = gql` type Movie @node { - id: ID! @id @unique + id: ID! @id title: String! actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } type Actor @node { - id: ID! @id @unique + id: ID! @id name: String! dob: DateTime! homeAddress: Point! @@ -57,14 +57,9 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { type ActorAggregateSelection { count: Int! dob: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } - input ActorConnectOrCreateWhere { - node: ActorUniqueWhere! - } - input ActorConnectWhere { node: ActorWhere! } @@ -80,19 +75,15 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { node: Actor! } - input ActorOnCreateInput { - dob: DateTime! - homeAddress: PointInput! - name: String! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -105,51 +96,46 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { name: SortDirection } - input ActorUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input ActorUpdateInput { - dob: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - dob_SET: DateTime - homeAddress: PointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - homeAddress_SET: PointInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + dob: DateTimeScalarMutations + dob_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'dob: { set: ... } }' instead.\\") + homeAddress: PointMutations + homeAddress_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'homeAddress: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - dob: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - dob_EQ: DateTime - dob_GT: DateTime - dob_GTE: DateTime - dob_IN: [DateTime!] - dob_LT: DateTime - dob_LTE: DateTime - homeAddress: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - homeAddress_DISTANCE: PointDistance - homeAddress_EQ: PointInput - homeAddress_GT: PointDistance - homeAddress_GTE: PointDistance - homeAddress_IN: [PointInput!] - homeAddress_LT: PointDistance - homeAddress_LTE: PointDistance - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + dob: DateTimeScalarFilters + dob_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter dob: { eq: ... }\\") + dob_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter dob: { gt: ... }\\") + dob_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter dob: { gte: ... }\\") + dob_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter dob: { in: ... }\\") + dob_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter dob: { lt: ... }\\") + dob_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter dob: { lte: ... }\\") + homeAddress: PointFilters + homeAddress_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { distance: ... }\\") + homeAddress_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { eq: ... }\\") + homeAddress_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { gt: ... }\\") + homeAddress_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { gte: ... }\\") + homeAddress_IN: [PointInput!] @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { in: ... }\\") + homeAddress_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { lt: ... }\\") + homeAddress_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter homeAddress: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -184,6 +170,27 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -192,15 +199,39 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID! title: String! } @@ -212,7 +243,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { type MovieActorActorsNodeAggregateSelection { dob: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -220,7 +250,7 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -230,28 +260,34 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - node: ActorOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -277,7 +313,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { input MovieActorsFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieActorsCreateFieldInput!] } @@ -285,41 +320,33 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - dob_MAX_EQUAL: DateTime - dob_MAX_GT: DateTime - dob_MAX_GTE: DateTime - dob_MAX_LT: DateTime - dob_MAX_LTE: DateTime - dob_MIN_EQUAL: DateTime - dob_MIN_GT: DateTime - dob_MIN_GTE: DateTime - dob_MIN_LT: DateTime - dob_MIN_LTE: DateTime - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + dob: DateTimeScalarAggregationFilters + dob_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { max: { eq: ... } } }' instead.\\") + dob_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { max: { gt: ... } } }' instead.\\") + dob_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { max: { gte: ... } } }' instead.\\") + dob_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { max: { lt: ... } } }' instead.\\") + dob_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { max: { lte: ... } } }' instead.\\") + dob_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { min: { eq: ... } } }' instead.\\") + dob_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { min: { gt: ... } } }' instead.\\") + dob_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { min: { gte: ... } } }' instead.\\") + dob_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { min: { lt: ... } } }' instead.\\") + dob_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'dob: { min: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -333,7 +360,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { input MovieActorsUpdateFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] create: [MovieActorsCreateFieldInput!] delete: [MovieActorsDeleteFieldInput!] disconnect: [MovieActorsDisconnectFieldInput!] @@ -343,7 +369,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -361,15 +386,6 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -380,51 +396,53 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -468,6 +486,23 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { point: PointInput! } + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + \\"\\"\\"Input type for a point\\"\\"\\" input PointInput { height: Float @@ -475,11 +510,16 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { longitude: Float! } + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -497,6 +537,27 @@ describe("https://github.com/neo4j/graphql/issues/1182", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/1575.test.ts b/packages/graphql/tests/schema/issues/1575.test.ts index 9cec38153d..afb47a4875 100644 --- a/packages/graphql/tests/schema/issues/1575.test.ts +++ b/packages/graphql/tests/schema/issues/1575.test.ts @@ -78,15 +78,6 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { node: Foo! } - input FooOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more FooSort objects to sort Foos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [FooSort!] - } - \\"\\"\\" Fields to sort Foos by. The order in which sorts are applied is not guaranteed when specifying many fields in one FooSort object. \\"\\"\\" @@ -96,32 +87,32 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { } input FooUpdateInput { - geo_point: PointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - geo_point_SET: PointInput - point: PointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - point_SET: PointInput + geo_point: PointMutations + geo_point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'geo_point: { set: ... } }' instead.\\") + point: PointMutations + point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'point: { set: ... } }' instead.\\") } input FooWhere { AND: [FooWhere!] NOT: FooWhere OR: [FooWhere!] - geo_point: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - geo_point_DISTANCE: PointDistance - geo_point_EQ: PointInput - geo_point_GT: PointDistance - geo_point_GTE: PointDistance - geo_point_IN: [PointInput] - geo_point_LT: PointDistance - geo_point_LTE: PointDistance - point: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - point_DISTANCE: PointDistance - point_EQ: PointInput - point_GT: PointDistance - point_GTE: PointDistance - point_IN: [PointInput] - point_LT: PointDistance - point_LTE: PointDistance + geo_point: PointFilters + geo_point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter geo_point: { distance: ... }\\") + geo_point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter geo_point: { eq: ... }\\") + geo_point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter geo_point: { gt: ... }\\") + geo_point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter geo_point: { gte: ... }\\") + geo_point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter geo_point: { in: ... }\\") + geo_point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter geo_point: { lt: ... }\\") + geo_point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter geo_point: { lte: ... }\\") + point: PointFilters + point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { distance: ... }\\") + point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter point: { eq: ... }\\") + point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gt: ... }\\") + point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gte: ... }\\") + point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter point: { in: ... }\\") + point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lt: ... }\\") + point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lte: ... }\\") } type FoosConnection { @@ -162,6 +153,23 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { point: PointInput! } + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + \\"\\"\\"Input type for a point\\"\\"\\" input PointInput { height: Float @@ -169,8 +177,13 @@ describe("https://github.com/neo4j/graphql/issues/1575", () => { longitude: Float! } + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + type Query { - foos(limit: Int, offset: Int, options: FooOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [FooSort!], where: FooWhere): [Foo!]! + foos(limit: Int, offset: Int, sort: [FooSort!], where: FooWhere): [Foo!]! foosAggregate(where: FooWhere): FooAggregateSelection! foosConnection(after: String, first: Int, sort: [FooSort!], where: FooWhere): FoosConnection! } diff --git a/packages/graphql/tests/schema/issues/1614.test.ts b/packages/graphql/tests/schema/issues/1614.test.ts index debe720f73..ebfbbab1c4 100644 --- a/packages/graphql/tests/schema/issues/1614.test.ts +++ b/packages/graphql/tests/schema/issues/1614.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } type CrewMember @node { - movies: Movie! @relationship(type: "WORKED_ON", direction: OUT, properties: "CrewPosition") + movies: [Movie!]! @relationship(type: "WORKED_ON", direction: OUT, properties: "CrewPosition") } `; @@ -72,9 +72,9 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } type CrewMember { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): Movie! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): CrewMemberMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [CrewMemberMoviesConnectionSort!], where: CrewMemberMoviesConnectionWhere): CrewMemberMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): CrewMemberMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [CrewMemberMoviesConnectionSort!], where: CrewMemberMoviesConnectionWhere): CrewMemberMoviesConnection! } type CrewMemberAggregateSelection { @@ -86,7 +86,7 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } input CrewMemberDeleteInput { - movies: CrewMemberMoviesDeleteFieldInput + movies: [CrewMemberMoviesDeleteFieldInput!] } type CrewMemberEdge { @@ -107,7 +107,7 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { AND: [CrewMemberMoviesAggregateInput!] NOT: CrewMemberMoviesAggregateInput OR: [CrewMemberMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -118,10 +118,6 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { input CrewMemberMoviesConnectFieldInput { edge: CrewPositionCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -131,6 +127,25 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { totalCount: Int! } + input CrewMemberMoviesConnectionFilters { + \\"\\"\\" + Return CrewMembers where all of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + all: CrewMemberMoviesConnectionWhere + \\"\\"\\" + Return CrewMembers where none of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + none: CrewMemberMoviesConnectionWhere + \\"\\"\\" + Return CrewMembers where one of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + single: CrewMemberMoviesConnectionWhere + \\"\\"\\" + Return CrewMembers where some of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + some: CrewMemberMoviesConnectionWhere + } + input CrewMemberMoviesConnectionSort { edge: CrewPositionSort node: MovieSort @@ -158,29 +173,30 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } input CrewMemberMoviesFieldInput { - connect: CrewMemberMoviesConnectFieldInput - create: CrewMemberMoviesCreateFieldInput + connect: [CrewMemberMoviesConnectFieldInput!] + create: [CrewMemberMoviesCreateFieldInput!] } input CrewMemberMoviesNodeAggregationWhereInput { AND: [CrewMemberMoviesNodeAggregationWhereInput!] NOT: CrewMemberMoviesNodeAggregationWhereInput OR: [CrewMemberMoviesNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type CrewMemberMoviesRelationship { @@ -195,30 +211,49 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } input CrewMemberMoviesUpdateFieldInput { - connect: CrewMemberMoviesConnectFieldInput - create: CrewMemberMoviesCreateFieldInput - delete: CrewMemberMoviesDeleteFieldInput - disconnect: CrewMemberMoviesDisconnectFieldInput + connect: [CrewMemberMoviesConnectFieldInput!] + create: [CrewMemberMoviesCreateFieldInput!] + delete: [CrewMemberMoviesDeleteFieldInput!] + disconnect: [CrewMemberMoviesDisconnectFieldInput!] update: CrewMemberMoviesUpdateConnectionInput where: CrewMemberMoviesConnectionWhere } - input CrewMemberOptions { - limit: Int - offset: Int - } - input CrewMemberUpdateInput { - movies: CrewMemberMoviesUpdateFieldInput + movies: [CrewMemberMoviesUpdateFieldInput!] } input CrewMemberWhere { AND: [CrewMemberWhere!] NOT: CrewMemberWhere OR: [CrewMemberWhere!] - movies: MovieWhere + movies: MovieRelationshipFilters moviesAggregate: CrewMemberMoviesAggregateInput - moviesConnection: CrewMemberMoviesConnectionWhere + moviesConnection: CrewMemberMoviesConnectionFilters + \\"\\"\\" + Return CrewMembers where all of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: CrewMemberMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return CrewMembers where none of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: CrewMemberMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return CrewMembers where one of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: CrewMemberMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return CrewMembers where some of the related CrewMemberMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: CrewMemberMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return CrewMembers where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return CrewMembers where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return CrewMembers where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return CrewMembers where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type CrewMembersConnection { @@ -249,18 +284,29 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { KeyGrip } + \\"\\"\\"CrewPositionType filters\\"\\"\\" + input CrewPositionTypeEnumScalarFilters { + eq: CrewPositionType + in: [CrewPositionType!] + } + + \\"\\"\\"CrewPositionType mutations\\"\\"\\" + input CrewPositionTypeEnumScalarMutations { + set: CrewPositionType + } + input CrewPositionUpdateInput { - position: CrewPositionType @deprecated(reason: \\"Please use the explicit _SET field\\") - position_SET: CrewPositionType + position: CrewPositionTypeEnumScalarMutations + position_SET: CrewPositionType @deprecated(reason: \\"Please use the generic mutation 'position: { set: ... } }' instead.\\") } input CrewPositionWhere { AND: [CrewPositionWhere!] NOT: CrewPositionWhere OR: [CrewPositionWhere!] - position: CrewPositionType @deprecated(reason: \\"Please use the explicit _EQ version\\") - position_EQ: CrewPositionType - position_IN: [CrewPositionType] + position: CrewPositionTypeEnumScalarFilters + position_EQ: CrewPositionType @deprecated(reason: \\"Please use the relevant generic filter position: { eq: ... }\\") + position_IN: [CrewPositionType] @deprecated(reason: \\"Please use the relevant generic filter position: { in: ... }\\") } \\"\\"\\" @@ -271,6 +317,26 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { name: String! } @@ -293,13 +359,15 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -310,20 +378,20 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } input MovieUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type MoviesConnection { @@ -350,10 +418,10 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { } type Query { - crewMembers(limit: Int, offset: Int, options: CrewMemberOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CrewMemberWhere): [CrewMember!]! + crewMembers(limit: Int, offset: Int, where: CrewMemberWhere): [CrewMember!]! crewMembersAggregate(where: CrewMemberWhere): CrewMemberAggregateSelection! crewMembersConnection(after: String, first: Int, where: CrewMemberWhere): CrewMembersConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -371,6 +439,27 @@ describe("https://github.com/neo4j/graphql/issues/1614", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateCrewMembersMutationResponse { crewMembers: [CrewMember!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/162.test.ts b/packages/graphql/tests/schema/issues/162.test.ts index a58c06f962..5b7a87661a 100644 --- a/packages/graphql/tests/schema/issues/162.test.ts +++ b/packages/graphql/tests/schema/issues/162.test.ts @@ -31,12 +31,12 @@ describe("162", () => { type TigerJawLevel2 @node { id: ID - part1: TigerJawLevel2Part1! @relationship(type: "REL1", direction: OUT) + part1: [TigerJawLevel2Part1!]! @relationship(type: "REL1", direction: OUT) } type TigerJawLevel2Part1 @node { id: ID - tiger: Tiger! @relationship(type: "REL2", direction: OUT) + tiger: [Tiger!]! @relationship(type: "REL2", direction: OUT) } `; const neoSchema = new Neo4jGraphQL({ typeDefs }); @@ -79,9 +79,28 @@ describe("162", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -91,6 +110,31 @@ describe("162", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Mutation { createTigerJawLevel2Part1s(input: [TigerJawLevel2Part1CreateInput!]!): CreateTigerJawLevel2Part1sMutationResponse! createTigerJawLevel2s(input: [TigerJawLevel2CreateInput!]!): CreateTigerJawLevel2sMutationResponse! @@ -112,13 +156,13 @@ describe("162", () => { } type Query { - tigerJawLevel2Part1s(limit: Int, offset: Int, options: TigerJawLevel2Part1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TigerJawLevel2Part1Sort!], where: TigerJawLevel2Part1Where): [TigerJawLevel2Part1!]! + tigerJawLevel2Part1s(limit: Int, offset: Int, sort: [TigerJawLevel2Part1Sort!], where: TigerJawLevel2Part1Where): [TigerJawLevel2Part1!]! tigerJawLevel2Part1sAggregate(where: TigerJawLevel2Part1Where): TigerJawLevel2Part1AggregateSelection! tigerJawLevel2Part1sConnection(after: String, first: Int, sort: [TigerJawLevel2Part1Sort!], where: TigerJawLevel2Part1Where): TigerJawLevel2Part1sConnection! - tigerJawLevel2s(limit: Int, offset: Int, options: TigerJawLevel2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TigerJawLevel2Sort!], where: TigerJawLevel2Where): [TigerJawLevel2!]! + tigerJawLevel2s(limit: Int, offset: Int, sort: [TigerJawLevel2Sort!], where: TigerJawLevel2Where): [TigerJawLevel2!]! tigerJawLevel2sAggregate(where: TigerJawLevel2Where): TigerJawLevel2AggregateSelection! tigerJawLevel2sConnection(after: String, first: Int, sort: [TigerJawLevel2Sort!], where: TigerJawLevel2Where): TigerJawLevel2sConnection! - tigers(limit: Int, offset: Int, options: TigerOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TigerSort!], where: TigerWhere): [Tiger!]! + tigers(limit: Int, offset: Int, sort: [TigerSort!], where: TigerWhere): [Tiger!]! tigersAggregate(where: TigerWhere): TigerAggregateSelection! tigersConnection(after: String, first: Int, sort: [TigerSort!], where: TigerWhere): TigersConnection! } @@ -155,14 +199,13 @@ describe("162", () => { type TigerJawLevel2 { id: ID - part1(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: TigerJawLevel2Part1Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TigerJawLevel2Part1Sort!], where: TigerJawLevel2Part1Where): TigerJawLevel2Part1! - part1Aggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: TigerJawLevel2Part1Where): TigerJawLevel2TigerJawLevel2Part1Part1AggregationSelection - part1Connection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [TigerJawLevel2Part1ConnectionSort!], where: TigerJawLevel2Part1ConnectionWhere): TigerJawLevel2Part1Connection! + part1(limit: Int, offset: Int, sort: [TigerJawLevel2Part1Sort!], where: TigerJawLevel2Part1Where): [TigerJawLevel2Part1!]! + part1Aggregate(where: TigerJawLevel2Part1Where): TigerJawLevel2TigerJawLevel2Part1Part1AggregationSelection + part1Connection(after: String, first: Int, sort: [TigerJawLevel2Part1ConnectionSort!], where: TigerJawLevel2Part1ConnectionWhere): TigerJawLevel2Part1Connection! } type TigerJawLevel2AggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input TigerJawLevel2CreateInput { @@ -171,7 +214,7 @@ describe("162", () => { } input TigerJawLevel2DeleteInput { - part1: TigerJawLevel2Part1DeleteFieldInput + part1: [TigerJawLevel2Part1DeleteFieldInput!] } type TigerJawLevel2Edge { @@ -179,51 +222,36 @@ describe("162", () => { node: TigerJawLevel2! } - input TigerJawLevel2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TigerJawLevel2Sort objects to sort TigerJawLevel2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TigerJawLevel2Sort!] - } - type TigerJawLevel2Part1 { id: ID - tiger(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: TigerOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TigerSort!], where: TigerWhere): Tiger! - tigerAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: TigerWhere): TigerJawLevel2Part1TigerTigerAggregationSelection - tigerConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [TigerJawLevel2Part1TigerConnectionSort!], where: TigerJawLevel2Part1TigerConnectionWhere): TigerJawLevel2Part1TigerConnection! + tiger(limit: Int, offset: Int, sort: [TigerSort!], where: TigerWhere): [Tiger!]! + tigerAggregate(where: TigerWhere): TigerJawLevel2Part1TigerTigerAggregationSelection + tigerConnection(after: String, first: Int, sort: [TigerJawLevel2Part1TigerConnectionSort!], where: TigerJawLevel2Part1TigerConnectionWhere): TigerJawLevel2Part1TigerConnection! } input TigerJawLevel2Part1AggregateInput { AND: [TigerJawLevel2Part1AggregateInput!] NOT: TigerJawLevel2Part1AggregateInput OR: [TigerJawLevel2Part1AggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: TigerJawLevel2Part1NodeAggregationWhereInput } type TigerJawLevel2Part1AggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input TigerJawLevel2Part1ConnectFieldInput { - connect: TigerJawLevel2Part1ConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [TigerJawLevel2Part1ConnectInput!] where: TigerJawLevel2Part1ConnectWhere } input TigerJawLevel2Part1ConnectInput { - tiger: TigerJawLevel2Part1TigerConnectFieldInput + tiger: [TigerJawLevel2Part1TigerConnectFieldInput!] } input TigerJawLevel2Part1ConnectWhere { @@ -236,6 +264,25 @@ describe("162", () => { totalCount: Int! } + input TigerJawLevel2Part1ConnectionFilters { + \\"\\"\\" + Return TigerJawLevel2s where all of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + all: TigerJawLevel2Part1ConnectionWhere + \\"\\"\\" + Return TigerJawLevel2s where none of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + none: TigerJawLevel2Part1ConnectionWhere + \\"\\"\\" + Return TigerJawLevel2s where one of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + single: TigerJawLevel2Part1ConnectionWhere + \\"\\"\\" + Return TigerJawLevel2s where some of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + some: TigerJawLevel2Part1ConnectionWhere + } + input TigerJawLevel2Part1ConnectionSort { node: TigerJawLevel2Part1Sort } @@ -262,7 +309,7 @@ describe("162", () => { } input TigerJawLevel2Part1DeleteInput { - tiger: TigerJawLevel2Part1TigerDeleteFieldInput + tiger: [TigerJawLevel2Part1TigerDeleteFieldInput!] } input TigerJawLevel2Part1DisconnectFieldInput { @@ -271,7 +318,7 @@ describe("162", () => { } input TigerJawLevel2Part1DisconnectInput { - tiger: TigerJawLevel2Part1TigerDisconnectFieldInput + tiger: [TigerJawLevel2Part1TigerDisconnectFieldInput!] } type TigerJawLevel2Part1Edge { @@ -280,38 +327,32 @@ describe("162", () => { } input TigerJawLevel2Part1FieldInput { - connect: TigerJawLevel2Part1ConnectFieldInput - create: TigerJawLevel2Part1CreateFieldInput + connect: [TigerJawLevel2Part1ConnectFieldInput!] + create: [TigerJawLevel2Part1CreateFieldInput!] } - input TigerJawLevel2Part1NodeAggregationWhereInput { - AND: [TigerJawLevel2Part1NodeAggregationWhereInput!] - NOT: TigerJawLevel2Part1NodeAggregationWhereInput - OR: [TigerJawLevel2Part1NodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + type TigerJawLevel2Part1Relationship { + cursor: String! + node: TigerJawLevel2Part1! } - input TigerJawLevel2Part1Options { - limit: Int - offset: Int + input TigerJawLevel2Part1RelationshipFilters { \\"\\"\\" - Specify one or more TigerJawLevel2Part1Sort objects to sort TigerJawLevel2Part1s by. The sorts will be applied in the order in which they are arranged in the array. + Filter type where all of the related TigerJawLevel2Part1s match this filter \\"\\"\\" - sort: [TigerJawLevel2Part1Sort!] - } - - type TigerJawLevel2Part1Relationship { - cursor: String! - node: TigerJawLevel2Part1! + all: TigerJawLevel2Part1Where + \\"\\"\\" + Filter type where none of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + none: TigerJawLevel2Part1Where + \\"\\"\\" + Filter type where one of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + single: TigerJawLevel2Part1Where + \\"\\"\\" + Filter type where some of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + some: TigerJawLevel2Part1Where } \\"\\"\\" @@ -325,7 +366,7 @@ describe("162", () => { AND: [TigerJawLevel2Part1TigerAggregateInput!] NOT: TigerJawLevel2Part1TigerAggregateInput OR: [TigerJawLevel2Part1TigerAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -335,10 +376,6 @@ describe("162", () => { } input TigerJawLevel2Part1TigerConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: TigerConnectWhere } @@ -348,6 +385,25 @@ describe("162", () => { totalCount: Int! } + input TigerJawLevel2Part1TigerConnectionFilters { + \\"\\"\\" + Return TigerJawLevel2Part1s where all of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + all: TigerJawLevel2Part1TigerConnectionWhere + \\"\\"\\" + Return TigerJawLevel2Part1s where none of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + none: TigerJawLevel2Part1TigerConnectionWhere + \\"\\"\\" + Return TigerJawLevel2Part1s where one of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + single: TigerJawLevel2Part1TigerConnectionWhere + \\"\\"\\" + Return TigerJawLevel2Part1s where some of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + some: TigerJawLevel2Part1TigerConnectionWhere + } + input TigerJawLevel2Part1TigerConnectionSort { node: TigerSort } @@ -372,34 +428,35 @@ describe("162", () => { } input TigerJawLevel2Part1TigerFieldInput { - connect: TigerJawLevel2Part1TigerConnectFieldInput - create: TigerJawLevel2Part1TigerCreateFieldInput + connect: [TigerJawLevel2Part1TigerConnectFieldInput!] + create: [TigerJawLevel2Part1TigerCreateFieldInput!] } input TigerJawLevel2Part1TigerNodeAggregationWhereInput { AND: [TigerJawLevel2Part1TigerNodeAggregationWhereInput!] NOT: TigerJawLevel2Part1TigerNodeAggregationWhereInput OR: [TigerJawLevel2Part1TigerNodeAggregationWhereInput!] - x_AVERAGE_EQUAL: Float - x_AVERAGE_GT: Float - x_AVERAGE_GTE: Float - x_AVERAGE_LT: Float - x_AVERAGE_LTE: Float - x_MAX_EQUAL: Int - x_MAX_GT: Int - x_MAX_GTE: Int - x_MAX_LT: Int - x_MAX_LTE: Int - x_MIN_EQUAL: Int - x_MIN_GT: Int - x_MIN_GTE: Int - x_MIN_LT: Int - x_MIN_LTE: Int - x_SUM_EQUAL: Int - x_SUM_GT: Int - x_SUM_GTE: Int - x_SUM_LT: Int - x_SUM_LTE: Int + x: IntScalarAggregationFilters + x_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'x: { average: { eq: ... } } }' instead.\\") + x_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'x: { average: { gt: ... } } }' instead.\\") + x_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'x: { average: { gte: ... } } }' instead.\\") + x_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'x: { average: { lt: ... } } }' instead.\\") + x_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'x: { average: { lte: ... } } }' instead.\\") + x_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { max: { eq: ... } } }' instead.\\") + x_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { max: { gt: ... } } }' instead.\\") + x_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { max: { gte: ... } } }' instead.\\") + x_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { max: { lt: ... } } }' instead.\\") + x_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { max: { lte: ... } } }' instead.\\") + x_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { min: { eq: ... } } }' instead.\\") + x_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { min: { gt: ... } } }' instead.\\") + x_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { min: { gte: ... } } }' instead.\\") + x_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { min: { lt: ... } } }' instead.\\") + x_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { min: { lte: ... } } }' instead.\\") + x_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { sum: { eq: ... } } }' instead.\\") + x_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { sum: { gt: ... } } }' instead.\\") + x_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { sum: { gte: ... } } }' instead.\\") + x_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { sum: { lt: ... } } }' instead.\\") + x_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'x: { sum: { lte: ... } } }' instead.\\") } type TigerJawLevel2Part1TigerRelationship { @@ -421,10 +478,10 @@ describe("162", () => { } input TigerJawLevel2Part1TigerUpdateFieldInput { - connect: TigerJawLevel2Part1TigerConnectFieldInput - create: TigerJawLevel2Part1TigerCreateFieldInput - delete: TigerJawLevel2Part1TigerDeleteFieldInput - disconnect: TigerJawLevel2Part1TigerDisconnectFieldInput + connect: [TigerJawLevel2Part1TigerConnectFieldInput!] + create: [TigerJawLevel2Part1TigerCreateFieldInput!] + delete: [TigerJawLevel2Part1TigerDeleteFieldInput!] + disconnect: [TigerJawLevel2Part1TigerDisconnectFieldInput!] update: TigerJawLevel2Part1TigerUpdateConnectionInput where: TigerJawLevel2Part1TigerConnectionWhere } @@ -434,33 +491,65 @@ describe("162", () => { } input TigerJawLevel2Part1UpdateFieldInput { - connect: TigerJawLevel2Part1ConnectFieldInput - create: TigerJawLevel2Part1CreateFieldInput - delete: TigerJawLevel2Part1DeleteFieldInput - disconnect: TigerJawLevel2Part1DisconnectFieldInput + connect: [TigerJawLevel2Part1ConnectFieldInput!] + create: [TigerJawLevel2Part1CreateFieldInput!] + delete: [TigerJawLevel2Part1DeleteFieldInput!] + disconnect: [TigerJawLevel2Part1DisconnectFieldInput!] update: TigerJawLevel2Part1UpdateConnectionInput where: TigerJawLevel2Part1ConnectionWhere } input TigerJawLevel2Part1UpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - tiger: TigerJawLevel2Part1TigerUpdateFieldInput + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + tiger: [TigerJawLevel2Part1TigerUpdateFieldInput!] } input TigerJawLevel2Part1Where { AND: [TigerJawLevel2Part1Where!] NOT: TigerJawLevel2Part1Where OR: [TigerJawLevel2Part1Where!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - tiger: TigerWhere + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + tiger: TigerRelationshipFilters tigerAggregate: TigerJawLevel2Part1TigerAggregateInput - tigerConnection: TigerJawLevel2Part1TigerConnectionWhere + tigerConnection: TigerJawLevel2Part1TigerConnectionFilters + \\"\\"\\" + Return TigerJawLevel2Part1s where all of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + tigerConnection_ALL: TigerJawLevel2Part1TigerConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'tigerConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where none of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + tigerConnection_NONE: TigerJawLevel2Part1TigerConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'tigerConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where one of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + tigerConnection_SINGLE: TigerJawLevel2Part1TigerConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'tigerConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where some of the related TigerJawLevel2Part1TigerConnections match this filter + \\"\\"\\" + tigerConnection_SOME: TigerJawLevel2Part1TigerConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'tigerConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where all of the related Tigers match this filter + \\"\\"\\" + tiger_ALL: TigerWhere @deprecated(reason: \\"Please use the relevant generic filter 'tiger: { all: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where none of the related Tigers match this filter + \\"\\"\\" + tiger_NONE: TigerWhere @deprecated(reason: \\"Please use the relevant generic filter 'tiger: { none: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where one of the related Tigers match this filter + \\"\\"\\" + tiger_SINGLE: TigerWhere @deprecated(reason: \\"Please use the relevant generic filter 'tiger: { single: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2Part1s where some of the related Tigers match this filter + \\"\\"\\" + tiger_SOME: TigerWhere @deprecated(reason: \\"Please use the relevant generic filter 'tiger: { some: ... }' instead.\\") } type TigerJawLevel2Part1sConnection { @@ -478,32 +567,59 @@ describe("162", () => { type TigerJawLevel2TigerJawLevel2Part1Part1AggregationSelection { count: Int! - node: TigerJawLevel2TigerJawLevel2Part1Part1NodeAggregateSelection - } - - type TigerJawLevel2TigerJawLevel2Part1Part1NodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input TigerJawLevel2UpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - part1: TigerJawLevel2Part1UpdateFieldInput + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + part1: [TigerJawLevel2Part1UpdateFieldInput!] } input TigerJawLevel2Where { AND: [TigerJawLevel2Where!] NOT: TigerJawLevel2Where OR: [TigerJawLevel2Where!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - part1: TigerJawLevel2Part1Where + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + part1: TigerJawLevel2Part1RelationshipFilters part1Aggregate: TigerJawLevel2Part1AggregateInput - part1Connection: TigerJawLevel2Part1ConnectionWhere + part1Connection: TigerJawLevel2Part1ConnectionFilters + \\"\\"\\" + Return TigerJawLevel2s where all of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + part1Connection_ALL: TigerJawLevel2Part1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'part1Connection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where none of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + part1Connection_NONE: TigerJawLevel2Part1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'part1Connection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where one of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + part1Connection_SINGLE: TigerJawLevel2Part1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'part1Connection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where some of the related TigerJawLevel2Part1Connections match this filter + \\"\\"\\" + part1Connection_SOME: TigerJawLevel2Part1ConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'part1Connection: { some: { node: ... } } }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where all of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + part1_ALL: TigerJawLevel2Part1Where @deprecated(reason: \\"Please use the relevant generic filter 'part1: { all: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where none of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + part1_NONE: TigerJawLevel2Part1Where @deprecated(reason: \\"Please use the relevant generic filter 'part1: { none: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where one of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + part1_SINGLE: TigerJawLevel2Part1Where @deprecated(reason: \\"Please use the relevant generic filter 'part1: { single: ... }' instead.\\") + \\"\\"\\" + Return TigerJawLevel2s where some of the related TigerJawLevel2Part1s match this filter + \\"\\"\\" + part1_SOME: TigerJawLevel2Part1Where @deprecated(reason: \\"Please use the relevant generic filter 'part1: { some: ... }' instead.\\") } type TigerJawLevel2sConnection { @@ -512,13 +628,15 @@ describe("162", () => { totalCount: Int! } - input TigerOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TigerSort objects to sort Tigers by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TigerSort!] + input TigerRelationshipFilters { + \\"\\"\\"Filter type where all of the related Tigers match this filter\\"\\"\\" + all: TigerWhere + \\"\\"\\"Filter type where none of the related Tigers match this filter\\"\\"\\" + none: TigerWhere + \\"\\"\\"Filter type where one of the related Tigers match this filter\\"\\"\\" + single: TigerWhere + \\"\\"\\"Filter type where some of the related Tigers match this filter\\"\\"\\" + some: TigerWhere } \\"\\"\\" @@ -529,23 +647,23 @@ describe("162", () => { } input TigerUpdateInput { - x: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - x_DECREMENT: Int - x_INCREMENT: Int - x_SET: Int + x: IntScalarMutations + x_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'x: { decrement: ... } }' instead.\\") + x_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'x: { increment: ... } }' instead.\\") + x_SET: Int @deprecated(reason: \\"Please use the generic mutation 'x: { set: ... } }' instead.\\") } input TigerWhere { AND: [TigerWhere!] NOT: TigerWhere OR: [TigerWhere!] - x: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - x_EQ: Int - x_GT: Int - x_GTE: Int - x_IN: [Int] - x_LT: Int - x_LTE: Int + x: IntScalarFilters + x_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter x: { eq: ... }\\") + x_GT: Int @deprecated(reason: \\"Please use the relevant generic filter x: { gt: ... }\\") + x_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter x: { gte: ... }\\") + x_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter x: { in: ... }\\") + x_LT: Int @deprecated(reason: \\"Please use the relevant generic filter x: { lt: ... }\\") + x_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter x: { lte: ... }\\") } type TigersConnection { diff --git a/packages/graphql/tests/schema/issues/200.test.ts b/packages/graphql/tests/schema/issues/200.test.ts index 870bd950d3..2f947de928 100644 --- a/packages/graphql/tests/schema/issues/200.test.ts +++ b/packages/graphql/tests/schema/issues/200.test.ts @@ -26,7 +26,7 @@ describe("200", () => { test("Preserve schema array non null", async () => { const typeDefs = gql` type Category @node { - categoryId: ID! @id @unique + categoryId: ID! @id name: String! description: String! @default(value: "") exampleImageLocations: [String!] @@ -55,7 +55,6 @@ describe("200", () => { } type CategoryAggregateSelection { - categoryId: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") count: Int! description: StringAggregateSelection! name: StringAggregateSelection! @@ -72,15 +71,6 @@ describe("200", () => { node: Category! } - input CategoryOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more CategorySort objects to sort Categories by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [CategorySort!] - } - \\"\\"\\" Fields to sort Categories by. The order in which sorts are applied is not guaranteed when specifying many fields in one CategorySort object. \\"\\"\\" @@ -91,41 +81,41 @@ describe("200", () => { } input CategoryUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - exampleImageLocations: [String!] @deprecated(reason: \\"Please use the explicit _SET field\\") - exampleImageLocations_POP: Int - exampleImageLocations_PUSH: [String!] - exampleImageLocations_SET: [String!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + exampleImageLocations: ListStringMutations + exampleImageLocations_POP: Int @deprecated(reason: \\"Please use the generic mutation 'exampleImageLocations: { pop: ... } }' instead.\\") + exampleImageLocations_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'exampleImageLocations: { push: ... } }' instead.\\") + exampleImageLocations_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'exampleImageLocations: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input CategoryWhere { AND: [CategoryWhere!] NOT: CategoryWhere OR: [CategoryWhere!] - categoryId: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - categoryId_CONTAINS: ID - categoryId_ENDS_WITH: ID - categoryId_EQ: ID - categoryId_IN: [ID!] - categoryId_STARTS_WITH: ID - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String!] - description_STARTS_WITH: String - exampleImageLocations: [String!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - exampleImageLocations_EQ: [String!] - exampleImageLocations_INCLUDES: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + categoryId: IDScalarFilters + categoryId_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter categoryId: { contains: ... }\\") + categoryId_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter categoryId: { endsWith: ... }\\") + categoryId_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter categoryId: { eq: ... }\\") + categoryId_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter categoryId: { in: ... }\\") + categoryId_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter categoryId: { startsWith: ... }\\") + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + exampleImageLocations: StringListFilters + exampleImageLocations_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter exampleImageLocations: { eq: ... }\\") + exampleImageLocations_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter exampleImageLocations: { includes: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type CreateCategoriesMutationResponse { @@ -149,9 +139,20 @@ describe("200", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] } type Mutation { @@ -169,7 +170,7 @@ describe("200", () => { } type Query { - categories(limit: Int, offset: Int, options: CategoryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [CategorySort!], where: CategoryWhere): [Category!]! + categories(limit: Int, offset: Int, sort: [CategorySort!], where: CategoryWhere): [Category!]! categoriesAggregate(where: CategoryWhere): CategoryAggregateSelection! categoriesConnection(after: String, first: Int, sort: [CategorySort!], where: CategoryWhere): CategoriesConnection! } @@ -187,6 +188,26 @@ describe("200", () => { shortest: String } + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateCategoriesMutationResponse { categories: [Category!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/2187.test.ts b/packages/graphql/tests/schema/issues/2187.test.ts index 71e8cf4dee..832aaf0b58 100644 --- a/packages/graphql/tests/schema/issues/2187.test.ts +++ b/packages/graphql/tests/schema/issues/2187.test.ts @@ -81,10 +81,37 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { sum: Float } + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + type Genre { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): GenreMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): GenreMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! name: String } @@ -134,7 +161,7 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { AND: [GenreMoviesAggregateInput!] NOT: GenreMoviesAggregateInput OR: [GenreMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -145,10 +172,6 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { input GenreMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -158,6 +181,25 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { totalCount: Int! } + input GenreMoviesConnectionFilters { + \\"\\"\\" + Return Genres where all of the related GenreMoviesConnections match this filter + \\"\\"\\" + all: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where none of the related GenreMoviesConnections match this filter + \\"\\"\\" + none: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where one of the related GenreMoviesConnections match this filter + \\"\\"\\" + single: GenreMoviesConnectionWhere + \\"\\"\\" + Return Genres where some of the related GenreMoviesConnections match this filter + \\"\\"\\" + some: GenreMoviesConnectionWhere + } + input GenreMoviesConnectionSort { node: MovieSort } @@ -192,61 +234,64 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { AND: [GenreMoviesNodeAggregationWhereInput!] NOT: GenreMoviesNodeAggregationWhereInput OR: [GenreMoviesNodeAggregationWhereInput!] - imdbRating_AVERAGE_EQUAL: Float - imdbRating_AVERAGE_GT: Float - imdbRating_AVERAGE_GTE: Float - imdbRating_AVERAGE_LT: Float - imdbRating_AVERAGE_LTE: Float - imdbRating_MAX_EQUAL: Float - imdbRating_MAX_GT: Float - imdbRating_MAX_GTE: Float - imdbRating_MAX_LT: Float - imdbRating_MAX_LTE: Float - imdbRating_MIN_EQUAL: Float - imdbRating_MIN_GT: Float - imdbRating_MIN_GTE: Float - imdbRating_MIN_LT: Float - imdbRating_MIN_LTE: Float - imdbRating_SUM_EQUAL: Float - imdbRating_SUM_GT: Float - imdbRating_SUM_GTE: Float - imdbRating_SUM_LT: Float - imdbRating_SUM_LTE: Float - title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Do not use title\\") - title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Do not use title\\") - title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Do not use title\\") - title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Do not use title\\") - title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Do not use title\\") - title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Do not use title\\") - title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Do not use title\\") - title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Do not use title\\") - title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Do not use title\\") - title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Do not use title\\") - title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Do not use title\\") - title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Do not use title\\") - title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Do not use title\\") - title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Do not use title\\") - title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Do not use title\\") - year_AVERAGE_EQUAL: Float - year_AVERAGE_GT: Float - year_AVERAGE_GTE: Float - year_AVERAGE_LT: Float - year_AVERAGE_LTE: Float - year_MAX_EQUAL: Int - year_MAX_GT: Int - year_MAX_GTE: Int - year_MAX_LT: Int - year_MAX_LTE: Int - year_MIN_EQUAL: Int - year_MIN_GT: Int - year_MIN_GTE: Int - year_MIN_LT: Int - year_MIN_LTE: Int - year_SUM_EQUAL: Int - year_SUM_GT: Int - year_SUM_GTE: Int - year_SUM_LT: Int - year_SUM_LTE: Int + imdbRating: FloatScalarAggregationFilters + imdbRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { eq: ... } } }' instead.\\") + imdbRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { gt: ... } } }' instead.\\") + imdbRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { gte: ... } } }' instead.\\") + imdbRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { lt: ... } } }' instead.\\") + imdbRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { average: { lte: ... } } }' instead.\\") + imdbRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { eq: ... } } }' instead.\\") + imdbRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { gt: ... } } }' instead.\\") + imdbRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { gte: ... } } }' instead.\\") + imdbRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { lt: ... } } }' instead.\\") + imdbRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { max: { lte: ... } } }' instead.\\") + imdbRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { eq: ... } } }' instead.\\") + imdbRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { gt: ... } } }' instead.\\") + imdbRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { gte: ... } } }' instead.\\") + imdbRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { lt: ... } } }' instead.\\") + imdbRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { min: { lte: ... } } }' instead.\\") + imdbRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { eq: ... } } }' instead.\\") + imdbRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { gt: ... } } }' instead.\\") + imdbRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { gte: ... } } }' instead.\\") + imdbRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { lt: ... } } }' instead.\\") + imdbRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbRating: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + year: IntScalarAggregationFilters + year_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { eq: ... } } }' instead.\\") + year_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gt: ... } } }' instead.\\") + year_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gte: ... } } }' instead.\\") + year_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lt: ... } } }' instead.\\") + year_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lte: ... } } }' instead.\\") + year_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { eq: ... } } }' instead.\\") + year_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gt: ... } } }' instead.\\") + year_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gte: ... } } }' instead.\\") + year_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lt: ... } } }' instead.\\") + year_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lte: ... } } }' instead.\\") + year_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { eq: ... } } }' instead.\\") + year_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gt: ... } } }' instead.\\") + year_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gte: ... } } }' instead.\\") + year_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lt: ... } } }' instead.\\") + year_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lte: ... } } }' instead.\\") + year_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { eq: ... } } }' instead.\\") + year_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gt: ... } } }' instead.\\") + year_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gte: ... } } }' instead.\\") + year_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lt: ... } } }' instead.\\") + year_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lte: ... } } }' instead.\\") } type GenreMoviesRelationship { @@ -267,13 +312,15 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { where: GenreMoviesConnectionWhere } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] + input GenreRelationshipFilters { + \\"\\"\\"Filter type where all of the related Genres match this filter\\"\\"\\" + all: GenreWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\"Filter type where none of the related Genres match this filter\\"\\"\\" + none: GenreWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\"Filter type where one of the related Genres match this filter\\"\\"\\" + single: GenreWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\"Filter type where some of the related Genres match this filter\\"\\"\\" + some: GenreWhere @deprecated(reason: \\"Do not use genre\\") } \\"\\"\\" @@ -285,45 +332,47 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { input GenreUpdateInput { movies: [GenreMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] + movies: MovieRelationshipFilters moviesAggregate: GenreMoviesAggregateInput + moviesConnection: GenreMoviesConnectionFilters \\"\\"\\" Return Genres where all of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: GenreMoviesConnectionWhere + moviesConnection_ALL: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where none of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: GenreMoviesConnectionWhere + moviesConnection_NONE: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where one of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: GenreMoviesConnectionWhere + moviesConnection_SINGLE: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where some of the related GenreMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: GenreMoviesConnectionWhere + moviesConnection_SOME: GenreMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Genres where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Genres where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Genres where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Genres where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type GenresConnection { @@ -339,10 +388,35 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - genres(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! @deprecated(reason: \\"Do not use genre\\") - genresAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenresAggregationSelection @deprecated(reason: \\"Do not use genre\\") - genresConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! @deprecated(reason: \\"Do not use genre\\") + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! @deprecated(reason: \\"Do not use genre\\") + genresAggregate(where: GenreWhere): MovieGenreGenresAggregationSelection @deprecated(reason: \\"Do not use genre\\") + genresConnection(after: String, first: Int, sort: [MovieGenresConnectionSort!], where: MovieGenresConnectionWhere): MovieGenresConnection! @deprecated(reason: \\"Do not use genre\\") imdbRating: Float title: String @deprecated(reason: \\"Do not use title\\") year: Int @@ -396,7 +470,7 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { AND: [MovieGenresAggregateInput!] NOT: MovieGenresAggregateInput OR: [MovieGenresAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -407,10 +481,6 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { input MovieGenresConnectFieldInput { connect: [GenreConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: GenreConnectWhere } @@ -420,6 +490,25 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { totalCount: Int! } + input MovieGenresConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieGenresConnections match this filter + \\"\\"\\" + all: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\" + Return Movies where none of the related MovieGenresConnections match this filter + \\"\\"\\" + none: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\" + Return Movies where one of the related MovieGenresConnections match this filter + \\"\\"\\" + single: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use genre\\") + \\"\\"\\" + Return Movies where some of the related MovieGenresConnections match this filter + \\"\\"\\" + some: MovieGenresConnectionWhere @deprecated(reason: \\"Do not use genre\\") + } + input MovieGenresConnectionSort { node: GenreSort } @@ -454,21 +543,22 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { AND: [MovieGenresNodeAggregationWhereInput!] NOT: MovieGenresNodeAggregationWhereInput OR: [MovieGenresNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieGenresRelationship { @@ -489,13 +579,15 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { where: MovieGenresConnectionWhere } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -503,31 +595,33 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { \\"\\"\\" input MovieSort { imdbRating: SortDirection - title: SortDirection @deprecated(reason: \\"Do not use title\\") + title: SortDirection year: SortDirection } input MovieUpdateInput { genres: [MovieGenresUpdateFieldInput!] @deprecated(reason: \\"Do not use genre\\") - imdbRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - imdbRating_ADD: Float - imdbRating_DIVIDE: Float - imdbRating_MULTIPLY: Float - imdbRating_SET: Float - imdbRating_SUBTRACT: Float - title: String @deprecated(reason: \\"Do not use title\\") + imdbRating: FloatScalarMutations + imdbRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { add: ... } }' instead.\\") + imdbRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { divide: ... } }' instead.\\") + imdbRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { multiply: ... } }' instead.\\") + imdbRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'imdbRating: { set: ... } }' instead.\\") + imdbRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { subtract: ... } }' instead.\\") + title: StringScalarMutations @deprecated(reason: \\"Do not use title\\") title_SET: String @deprecated(reason: \\"Do not use title\\") - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int + year: IntScalarMutations + year_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { decrement: ... } }' instead.\\") + year_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { increment: ... } }' instead.\\") + year_SET: Int @deprecated(reason: \\"Please use the generic mutation 'year: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + genres: GenreRelationshipFilters genresAggregate: MovieGenresAggregateInput @deprecated(reason: \\"Do not use genre\\") + genresConnection: MovieGenresConnectionFilters \\"\\"\\" Return Movies where all of the related MovieGenresConnections match this filter \\"\\"\\" @@ -552,26 +646,26 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { genres_SINGLE: GenreWhere @deprecated(reason: \\"Do not use genre\\") \\"\\"\\"Return Movies where some of the related Genres match this filter\\"\\"\\" genres_SOME: GenreWhere @deprecated(reason: \\"Do not use genre\\") - imdbRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbRating_EQ: Float - imdbRating_GT: Float - imdbRating_GTE: Float - imdbRating_IN: [Float] - imdbRating_LT: Float - imdbRating_LTE: Float - title: String @deprecated(reason: \\"Do not use title\\") + imdbRating: FloatScalarFilters + imdbRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { eq: ... }\\") + imdbRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gt: ... }\\") + imdbRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gte: ... }\\") + imdbRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { in: ... }\\") + imdbRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lt: ... }\\") + imdbRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lte: ... }\\") + title: StringScalarFilters @deprecated(reason: \\"Do not use title\\") title_CONTAINS: String @deprecated(reason: \\"Do not use title\\") title_ENDS_WITH: String @deprecated(reason: \\"Do not use title\\") title_EQ: String @deprecated(reason: \\"Do not use title\\") title_IN: [String] @deprecated(reason: \\"Do not use title\\") title_STARTS_WITH: String @deprecated(reason: \\"Do not use title\\") - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int] - year_LT: Int - year_LTE: Int + year: IntScalarFilters + year_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter year: { eq: ... }\\") + year_GT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gt: ... }\\") + year_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gte: ... }\\") + year_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter year: { in: ... }\\") + year_LT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lt: ... }\\") + year_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lte: ... }\\") } type MoviesConnection { @@ -598,10 +692,10 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -619,6 +713,27 @@ describe("https://github.com/neo4j/graphql/issues/2187", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateGenresMutationResponse { genres: [Genre!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/2377.test.ts b/packages/graphql/tests/schema/issues/2377.test.ts index 49c0e27f42..93a4a6b752 100644 --- a/packages/graphql/tests/schema/issues/2377.test.ts +++ b/packages/graphql/tests/schema/issues/2377.test.ts @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { } type Resource implements ResourceEntity @node { - id: ID! @unique + id: ID! name: String type: ResourceType! externalIds: [ID!] @@ -105,6 +105,27 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -113,9 +134,51 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID list filters\\"\\"\\" + input IDListFilters { + eq: [ID!] + includes: ID + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for ID\\"\\"\\" + input ListIDMutations { + pop: Int + push: [ID!] + set: [ID!] } type Mutation { @@ -138,11 +201,24 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { PropertyC } + \\"\\"\\"Property filters\\"\\"\\" + input PropertyListEnumScalarFilters { + eq: [Property!] + includes: Property + } + + \\"\\"\\"Mutations for a list for Property\\"\\"\\" + input PropertyListEnumScalarMutations { + pop: Property + push: [Property!]! + set: [Property!]! + } + type Query { - resourceEntities(limit: Int, offset: Int, options: ResourceEntityOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ResourceEntitySort!], where: ResourceEntityWhere): [ResourceEntity!]! + resourceEntities(limit: Int, offset: Int, sort: [ResourceEntitySort!], where: ResourceEntityWhere): [ResourceEntity!]! resourceEntitiesAggregate(where: ResourceEntityWhere): ResourceEntityAggregateSelection! resourceEntitiesConnection(after: String, first: Int, sort: [ResourceEntitySort!], where: ResourceEntityWhere): ResourceEntitiesConnection! - resources(limit: Int, offset: Int, options: ResourceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ResourceSort!], where: ResourceWhere): [Resource!]! + resources(limit: Int, offset: Int, sort: [ResourceSort!], where: ResourceWhere): [Resource!]! resourcesAggregate(where: ResourceWhere): ResourceAggregateSelection! resourcesConnection(after: String, first: Int, sort: [ResourceSort!], where: ResourceWhere): ResourcesConnection! } @@ -151,9 +227,9 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { \\"\\"\\" Resources encapsulating the given resource (e.g., a github org contains a repo) \\"\\"\\" - containedBy(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ResourceOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ResourceSort!], where: ResourceWhere): [Resource!]! - containedByAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ResourceWhere): ResourceResourceContainedByAggregationSelection - containedByConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ResourceContainedByConnectionSort!], where: ResourceContainedByConnectionWhere): ResourceContainedByConnection! + containedBy(limit: Int, offset: Int, sort: [ResourceSort!], where: ResourceWhere): [Resource!]! + containedByAggregate(where: ResourceWhere): ResourceResourceContainedByAggregationSelection + containedByConnection(after: String, first: Int, sort: [ResourceContainedByConnectionSort!], where: ResourceContainedByConnectionWhere): ResourceContainedByConnection! createdAt: DateTime! externalIds: [ID!] id: ID! @@ -168,7 +244,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { type ResourceAggregateSelection { count: Int! createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! updatedAt: DateTimeAggregateSelection! } @@ -177,10 +252,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { containedBy: [ResourceContainedByConnectFieldInput!] } - input ResourceConnectOrCreateWhere { - node: ResourceUniqueWhere! - } - input ResourceConnectWhere { node: ResourceWhere! } @@ -189,7 +260,7 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { AND: [ResourceContainedByAggregateInput!] NOT: ResourceContainedByAggregateInput OR: [ResourceContainedByAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -200,28 +271,34 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { input ResourceContainedByConnectFieldInput { connect: [ResourceConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ResourceConnectWhere } - input ResourceContainedByConnectOrCreateFieldInput { - onCreate: ResourceContainedByConnectOrCreateFieldInputOnCreate! - where: ResourceConnectOrCreateWhere! - } - - input ResourceContainedByConnectOrCreateFieldInputOnCreate { - node: ResourceOnCreateInput! - } - type ResourceContainedByConnection { edges: [ResourceContainedByRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input ResourceContainedByConnectionFilters { + \\"\\"\\" + Return Resources where all of the related ResourceContainedByConnections match this filter + \\"\\"\\" + all: ResourceContainedByConnectionWhere + \\"\\"\\" + Return Resources where none of the related ResourceContainedByConnections match this filter + \\"\\"\\" + none: ResourceContainedByConnectionWhere + \\"\\"\\" + Return Resources where one of the related ResourceContainedByConnections match this filter + \\"\\"\\" + single: ResourceContainedByConnectionWhere + \\"\\"\\" + Return Resources where some of the related ResourceContainedByConnections match this filter + \\"\\"\\" + some: ResourceContainedByConnectionWhere + } + input ResourceContainedByConnectionSort { node: ResourceSort } @@ -249,7 +326,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { input ResourceContainedByFieldInput { connect: [ResourceContainedByConnectFieldInput!] - connectOrCreate: [ResourceContainedByConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ResourceContainedByCreateFieldInput!] } @@ -257,51 +333,44 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { AND: [ResourceContainedByNodeAggregationWhereInput!] NOT: ResourceContainedByNodeAggregationWhereInput OR: [ResourceContainedByNodeAggregationWhereInput!] - createdAt_MAX_EQUAL: DateTime - createdAt_MAX_GT: DateTime - createdAt_MAX_GTE: DateTime - createdAt_MAX_LT: DateTime - createdAt_MAX_LTE: DateTime - createdAt_MIN_EQUAL: DateTime - createdAt_MIN_GT: DateTime - createdAt_MIN_GTE: DateTime - createdAt_MIN_LT: DateTime - createdAt_MIN_LTE: DateTime - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - updatedAt_MAX_EQUAL: DateTime - updatedAt_MAX_GT: DateTime - updatedAt_MAX_GTE: DateTime - updatedAt_MAX_LT: DateTime - updatedAt_MAX_LTE: DateTime - updatedAt_MIN_EQUAL: DateTime - updatedAt_MIN_GT: DateTime - updatedAt_MIN_GTE: DateTime - updatedAt_MIN_LT: DateTime - updatedAt_MIN_LTE: DateTime + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + updatedAt: DateTimeScalarAggregationFilters + updatedAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { max: { eq: ... } } }' instead.\\") + updatedAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { max: { gt: ... } } }' instead.\\") + updatedAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { max: { gte: ... } } }' instead.\\") + updatedAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { max: { lt: ... } } }' instead.\\") + updatedAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { max: { lte: ... } } }' instead.\\") + updatedAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { min: { eq: ... } } }' instead.\\") + updatedAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { min: { gt: ... } } }' instead.\\") + updatedAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { min: { gte: ... } } }' instead.\\") + updatedAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { min: { lt: ... } } }' instead.\\") + updatedAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'updatedAt: { min: { lte: ... } } }' instead.\\") } type ResourceContainedByRelationship { @@ -315,7 +384,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { input ResourceContainedByUpdateFieldInput { connect: [ResourceContainedByConnectFieldInput!] - connectOrCreate: [ResourceContainedByConnectOrCreateFieldInput!] create: [ResourceContainedByCreateFieldInput!] delete: [ResourceContainedByDeleteFieldInput!] disconnect: [ResourceContainedByDisconnectFieldInput!] @@ -365,7 +433,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { type ResourceEntityAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -378,15 +445,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { Resource } - input ResourceEntityOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ResourceEntitySort objects to sort ResourceEntities by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ResourceEntitySort!] - } - \\"\\"\\" Fields to sort ResourceEntities by. The order in which sorts are applied is not guaranteed when specifying many fields in one ResourceEntitySort object. \\"\\"\\" @@ -400,48 +458,39 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { AND: [ResourceEntityWhere!] NOT: ResourceEntityWhere OR: [ResourceEntityWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - properties: [Property!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - properties_EQ: [Property!] - properties_INCLUDES: Property - tags: [Tag!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - tags_EQ: [Tag!] - tags_INCLUDES: Tag - type: ResourceType @deprecated(reason: \\"Please use the explicit _EQ version\\") - type_EQ: ResourceType - type_IN: [ResourceType!] + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + properties: PropertyListEnumScalarFilters + properties_EQ: [Property!] @deprecated(reason: \\"Please use the relevant generic filter properties: { eq: ... }\\") + properties_INCLUDES: Property @deprecated(reason: \\"Please use the relevant generic filter properties: { includes: ... }\\") + tags: TagListEnumScalarFilters + tags_EQ: [Tag!] @deprecated(reason: \\"Please use the relevant generic filter tags: { eq: ... }\\") + tags_INCLUDES: Tag @deprecated(reason: \\"Please use the relevant generic filter tags: { includes: ... }\\") + type: ResourceTypeEnumScalarFilters + type_EQ: ResourceType @deprecated(reason: \\"Please use the relevant generic filter type: { eq: ... }\\") + type_IN: [ResourceType!] @deprecated(reason: \\"Please use the relevant generic filter type: { in: ... }\\") typename: [ResourceEntityImplementation!] - typename_IN: [ResourceEntityImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } - input ResourceOnCreateInput { - externalIds: [ID!] - id: ID! - name: String - properties: [Property!] - tags: [Tag!] - type: ResourceType! - updatedAt: DateTime! - } - - input ResourceOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ResourceSort objects to sort Resources by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ResourceSort!] + input ResourceRelationshipFilters { + \\"\\"\\"Filter type where all of the related Resources match this filter\\"\\"\\" + all: ResourceWhere + \\"\\"\\"Filter type where none of the related Resources match this filter\\"\\"\\" + none: ResourceWhere + \\"\\"\\"Filter type where one of the related Resources match this filter\\"\\"\\" + single: ResourceWhere + \\"\\"\\"Filter type where some of the related Resources match this filter\\"\\"\\" + some: ResourceWhere } type ResourceResourceContainedByAggregationSelection { @@ -451,7 +500,6 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { type ResourceResourceContainedByNodeAggregateSelection { createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! updatedAt: DateTimeAggregateSelection! } @@ -473,98 +521,106 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { ResourceC } - input ResourceUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID + \\"\\"\\"ResourceType filters\\"\\"\\" + input ResourceTypeEnumScalarFilters { + eq: ResourceType + in: [ResourceType!] + } + + \\"\\"\\"ResourceType mutations\\"\\"\\" + input ResourceTypeEnumScalarMutations { + set: ResourceType } input ResourceUpdateInput { containedBy: [ResourceContainedByUpdateFieldInput!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - externalIds: [ID!] @deprecated(reason: \\"Please use the explicit _SET field\\") - externalIds_POP: Int - externalIds_PUSH: [ID!] - externalIds_SET: [ID!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - properties: [Property!] @deprecated(reason: \\"Please use the explicit _SET field\\") - properties_SET: [Property!] - tags: [Tag!] @deprecated(reason: \\"Please use the explicit _SET field\\") - tags_SET: [Tag!] - type: ResourceType @deprecated(reason: \\"Please use the explicit _SET field\\") - type_SET: ResourceType + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + externalIds: ListIDMutations + externalIds_POP: Int @deprecated(reason: \\"Please use the generic mutation 'externalIds: { pop: ... } }' instead.\\") + externalIds_PUSH: [ID!] @deprecated(reason: \\"Please use the generic mutation 'externalIds: { push: ... } }' instead.\\") + externalIds_SET: [ID!] @deprecated(reason: \\"Please use the generic mutation 'externalIds: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + properties: PropertyListEnumScalarMutations + properties_SET: [Property!] @deprecated(reason: \\"Please use the generic mutation 'properties: { set: ... } }' instead.\\") + tags: TagListEnumScalarMutations + tags_SET: [Tag!] @deprecated(reason: \\"Please use the generic mutation 'tags: { set: ... } }' instead.\\") + type: ResourceTypeEnumScalarMutations + type_SET: ResourceType @deprecated(reason: \\"Please use the generic mutation 'type: { set: ... } }' instead.\\") } input ResourceWhere { AND: [ResourceWhere!] NOT: ResourceWhere OR: [ResourceWhere!] + containedBy: ResourceRelationshipFilters containedByAggregate: ResourceContainedByAggregateInput + containedByConnection: ResourceContainedByConnectionFilters \\"\\"\\" Return Resources where all of the related ResourceContainedByConnections match this filter \\"\\"\\" - containedByConnection_ALL: ResourceContainedByConnectionWhere + containedByConnection_ALL: ResourceContainedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedByConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Resources where none of the related ResourceContainedByConnections match this filter \\"\\"\\" - containedByConnection_NONE: ResourceContainedByConnectionWhere + containedByConnection_NONE: ResourceContainedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedByConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Resources where one of the related ResourceContainedByConnections match this filter \\"\\"\\" - containedByConnection_SINGLE: ResourceContainedByConnectionWhere + containedByConnection_SINGLE: ResourceContainedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedByConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Resources where some of the related ResourceContainedByConnections match this filter \\"\\"\\" - containedByConnection_SOME: ResourceContainedByConnectionWhere + containedByConnection_SOME: ResourceContainedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedByConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Resources where all of the related Resources match this filter\\"\\"\\" - containedBy_ALL: ResourceWhere + containedBy_ALL: ResourceWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedBy: { all: ... }' instead.\\") \\"\\"\\"Return Resources where none of the related Resources match this filter\\"\\"\\" - containedBy_NONE: ResourceWhere + containedBy_NONE: ResourceWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedBy: { none: ... }' instead.\\") \\"\\"\\"Return Resources where one of the related Resources match this filter\\"\\"\\" - containedBy_SINGLE: ResourceWhere + containedBy_SINGLE: ResourceWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedBy: { single: ... }' instead.\\") \\"\\"\\"Return Resources where some of the related Resources match this filter\\"\\"\\" - containedBy_SOME: ResourceWhere - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime!] - createdAt_LT: DateTime - createdAt_LTE: DateTime - externalIds: [ID!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - externalIds_EQ: [ID!] - externalIds_INCLUDES: ID - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - properties: [Property!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - properties_EQ: [Property!] - properties_INCLUDES: Property - tags: [Tag!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - tags_EQ: [Tag!] - tags_INCLUDES: Tag - type: ResourceType @deprecated(reason: \\"Please use the explicit _EQ version\\") - type_EQ: ResourceType - type_IN: [ResourceType!] - updatedAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - updatedAt_EQ: DateTime - updatedAt_GT: DateTime - updatedAt_GTE: DateTime - updatedAt_IN: [DateTime!] - updatedAt_LT: DateTime - updatedAt_LTE: DateTime + containedBy_SOME: ResourceWhere @deprecated(reason: \\"Please use the relevant generic filter 'containedBy: { some: ... }' instead.\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + externalIds: IDListFilters + externalIds_EQ: [ID!] @deprecated(reason: \\"Please use the relevant generic filter externalIds: { eq: ... }\\") + externalIds_INCLUDES: ID @deprecated(reason: \\"Please use the relevant generic filter externalIds: { includes: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + properties: PropertyListEnumScalarFilters + properties_EQ: [Property!] @deprecated(reason: \\"Please use the relevant generic filter properties: { eq: ... }\\") + properties_INCLUDES: Property @deprecated(reason: \\"Please use the relevant generic filter properties: { includes: ... }\\") + tags: TagListEnumScalarFilters + tags_EQ: [Tag!] @deprecated(reason: \\"Please use the relevant generic filter tags: { eq: ... }\\") + tags_INCLUDES: Tag @deprecated(reason: \\"Please use the relevant generic filter tags: { includes: ... }\\") + type: ResourceTypeEnumScalarFilters + type_EQ: ResourceType @deprecated(reason: \\"Please use the relevant generic filter type: { eq: ... }\\") + type_IN: [ResourceType!] @deprecated(reason: \\"Please use the relevant generic filter type: { in: ... }\\") + updatedAt: DateTimeScalarFilters + updatedAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { eq: ... }\\") + updatedAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { gt: ... }\\") + updatedAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { gte: ... }\\") + updatedAt_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { in: ... }\\") + updatedAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { lt: ... }\\") + updatedAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter updatedAt: { lte: ... }\\") } type ResourcesConnection { @@ -586,12 +642,46 @@ describe("https://github.com/neo4j/graphql/issues/2377", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + enum Tag { TagA TagB TagC } + \\"\\"\\"Tag filters\\"\\"\\" + input TagListEnumScalarFilters { + eq: [Tag!] + includes: Tag + } + + \\"\\"\\"Mutations for a list for Tag\\"\\"\\" + input TagListEnumScalarMutations { + pop: Tag + push: [Tag!]! + set: [Tag!]! + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/issues/2969.test.ts b/packages/graphql/tests/schema/issues/2969.test.ts deleted file mode 100644 index daf9ccf34a..0000000000 --- a/packages/graphql/tests/schema/issues/2969.test.ts +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import gql from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("https://github.com/neo4j/graphql/issues/2969", () => { - test("authorAggregate should not be generated", async () => { - const typeDefs = gql` - type Post @node { - content: String! - author: User! @relationship(type: "HAS_AUTHOR", direction: OUT) - } - - type User @node { - id: ID! - name: String! - posts: [Post!]! @relationship(type: "HAS_AUTHOR", direction: IN) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreatePostsMutationResponse { - info: CreateInfo! - posts: [Post!]! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Mutation { - createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Post { - author(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User! - authorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserAuthorAggregationSelection - authorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostAuthorConnectionSort!], where: PostAuthorConnectionWhere): PostAuthorConnection! - content: String! - } - - type PostAggregateSelection { - content: StringAggregateSelection! - count: Int! - } - - input PostAuthorAggregateInput { - AND: [PostAuthorAggregateInput!] - NOT: PostAuthorAggregateInput - OR: [PostAuthorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: PostAuthorNodeAggregationWhereInput - } - - input PostAuthorConnectFieldInput { - connect: UserConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type PostAuthorConnection { - edges: [PostAuthorRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input PostAuthorConnectionSort { - node: UserSort - } - - input PostAuthorConnectionWhere { - AND: [PostAuthorConnectionWhere!] - NOT: PostAuthorConnectionWhere - OR: [PostAuthorConnectionWhere!] - node: UserWhere - } - - input PostAuthorCreateFieldInput { - node: UserCreateInput! - } - - input PostAuthorDeleteFieldInput { - delete: UserDeleteInput - where: PostAuthorConnectionWhere - } - - input PostAuthorDisconnectFieldInput { - disconnect: UserDisconnectInput - where: PostAuthorConnectionWhere - } - - input PostAuthorFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput - } - - input PostAuthorNodeAggregationWhereInput { - AND: [PostAuthorNodeAggregationWhereInput!] - NOT: PostAuthorNodeAggregationWhereInput - OR: [PostAuthorNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type PostAuthorRelationship { - cursor: String! - node: User! - } - - input PostAuthorUpdateConnectionInput { - node: UserUpdateInput - } - - input PostAuthorUpdateFieldInput { - connect: PostAuthorConnectFieldInput - create: PostAuthorCreateFieldInput - delete: PostAuthorDeleteFieldInput - disconnect: PostAuthorDisconnectFieldInput - update: PostAuthorUpdateConnectionInput - where: PostAuthorConnectionWhere - } - - input PostConnectInput { - author: PostAuthorConnectFieldInput - } - - input PostConnectWhere { - node: PostWhere! - } - - input PostCreateInput { - author: PostAuthorFieldInput - content: String! - } - - input PostDeleteInput { - author: PostAuthorDeleteFieldInput - } - - input PostDisconnectInput { - author: PostAuthorDisconnectFieldInput - } - - type PostEdge { - cursor: String! - node: Post! - } - - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - - \\"\\"\\" - Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. - \\"\\"\\" - input PostSort { - content: SortDirection - } - - input PostUpdateInput { - author: PostAuthorUpdateFieldInput - content: String @deprecated(reason: \\"Please use the explicit _SET field\\") - content_SET: String - } - - type PostUserAuthorAggregationSelection { - count: Int! - node: PostUserAuthorNodeAggregateSelection - } - - type PostUserAuthorNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name: StringAggregateSelection! - } - - input PostWhere { - AND: [PostWhere!] - NOT: PostWhere - OR: [PostWhere!] - author: UserWhere - authorAggregate: PostAuthorAggregateInput - authorConnection: PostAuthorConnectionWhere - content: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - content_CONTAINS: String - content_ENDS_WITH: String - content_EQ: String - content_IN: [String!] - content_STARTS_WITH: String - } - - type PostsConnection { - edges: [PostEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(where: PostWhere): PostAggregateSelection! - postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdatePostsMutationResponse { - info: UpdateInfo! - posts: [Post!]! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - id: ID! - name: String! - posts(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PostWhere): UserPostPostsAggregationSelection - postsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserPostsConnectionSort!], where: UserPostsConnectionWhere): UserPostsConnection! - } - - type UserAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name: StringAggregateSelection! - } - - input UserConnectInput { - posts: [UserPostsConnectFieldInput!] - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - id: ID! - name: String! - posts: UserPostsFieldInput - } - - input UserDeleteInput { - posts: [UserPostsDeleteFieldInput!] - } - - input UserDisconnectInput { - posts: [UserPostsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - type UserPostPostsAggregationSelection { - count: Int! - node: UserPostPostsNodeAggregateSelection - } - - type UserPostPostsNodeAggregateSelection { - content: StringAggregateSelection! - } - - input UserPostsAggregateInput { - AND: [UserPostsAggregateInput!] - NOT: UserPostsAggregateInput - OR: [UserPostsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserPostsNodeAggregationWhereInput - } - - input UserPostsConnectFieldInput { - connect: [PostConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: PostConnectWhere - } - - type UserPostsConnection { - edges: [UserPostsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserPostsConnectionSort { - node: PostSort - } - - input UserPostsConnectionWhere { - AND: [UserPostsConnectionWhere!] - NOT: UserPostsConnectionWhere - OR: [UserPostsConnectionWhere!] - node: PostWhere - } - - input UserPostsCreateFieldInput { - node: PostCreateInput! - } - - input UserPostsDeleteFieldInput { - delete: PostDeleteInput - where: UserPostsConnectionWhere - } - - input UserPostsDisconnectFieldInput { - disconnect: PostDisconnectInput - where: UserPostsConnectionWhere - } - - input UserPostsFieldInput { - connect: [UserPostsConnectFieldInput!] - create: [UserPostsCreateFieldInput!] - } - - input UserPostsNodeAggregationWhereInput { - AND: [UserPostsNodeAggregationWhereInput!] - NOT: UserPostsNodeAggregationWhereInput - OR: [UserPostsNodeAggregationWhereInput!] - content_AVERAGE_LENGTH_EQUAL: Float - content_AVERAGE_LENGTH_GT: Float - content_AVERAGE_LENGTH_GTE: Float - content_AVERAGE_LENGTH_LT: Float - content_AVERAGE_LENGTH_LTE: Float - content_LONGEST_LENGTH_EQUAL: Int - content_LONGEST_LENGTH_GT: Int - content_LONGEST_LENGTH_GTE: Int - content_LONGEST_LENGTH_LT: Int - content_LONGEST_LENGTH_LTE: Int - content_SHORTEST_LENGTH_EQUAL: Int - content_SHORTEST_LENGTH_GT: Int - content_SHORTEST_LENGTH_GTE: Int - content_SHORTEST_LENGTH_LT: Int - content_SHORTEST_LENGTH_LTE: Int - } - - type UserPostsRelationship { - cursor: String! - node: Post! - } - - input UserPostsUpdateConnectionInput { - node: PostUpdateInput - } - - input UserPostsUpdateFieldInput { - connect: [UserPostsConnectFieldInput!] - create: [UserPostsCreateFieldInput!] - delete: [UserPostsDeleteFieldInput!] - disconnect: [UserPostsDisconnectFieldInput!] - update: UserPostsUpdateConnectionInput - where: UserPostsConnectionWhere - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - id: SortDirection - name: SortDirection - } - - input UserUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - posts: [UserPostsUpdateFieldInput!] - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - postsAggregate: UserPostsAggregateInput - \\"\\"\\" - Return Users where all of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_ALL: UserPostsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_NONE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SINGLE: UserPostsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserPostsConnections match this filter - \\"\\"\\" - postsConnection_SOME: UserPostsConnectionWhere - \\"\\"\\"Return Users where all of the related Posts match this filter\\"\\"\\" - posts_ALL: PostWhere - \\"\\"\\"Return Users where none of the related Posts match this filter\\"\\"\\" - posts_NONE: PostWhere - \\"\\"\\"Return Users where one of the related Posts match this filter\\"\\"\\" - posts_SINGLE: PostWhere - \\"\\"\\"Return Users where some of the related Posts match this filter\\"\\"\\" - posts_SOME: PostWhere - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/issues/2981.test.ts b/packages/graphql/tests/schema/issues/2981.test.ts deleted file mode 100644 index dbb59a67ca..0000000000 --- a/packages/graphql/tests/schema/issues/2981.test.ts +++ /dev/null @@ -1,797 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("https://github.com/neo4j/graphql/issues/2981", () => { - test("BookTranslatedTitleCreateFieldInput fields should not be of type List", async () => { - const typeDefs = gql` - type Book @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - union BookTitle = BookTitle_SV | BookTitle_EN - - type BookTitle_SV @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type BookTitle_EN @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Book { - isbn: String! - originalTitle: String! - translatedTitle(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: BookTitleWhere): BookTitle - translatedTitleConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: BookTranslatedTitleConnectionWhere): BookTranslatedTitleConnection! - } - - type BookAggregateSelection { - count: Int! - isbn: StringAggregateSelection! - originalTitle: StringAggregateSelection! - } - - input BookConnectInput { - translatedTitle: BookTranslatedTitleConnectInput - } - - input BookConnectWhere { - node: BookWhere! - } - - input BookCreateInput { - isbn: String! - originalTitle: String! - translatedTitle: BookTranslatedTitleCreateInput - } - - input BookDeleteInput { - translatedTitle: BookTranslatedTitleDeleteInput - } - - input BookDisconnectInput { - translatedTitle: BookTranslatedTitleDisconnectInput - } - - type BookEdge { - cursor: String! - node: Book! - } - - input BookOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BookSort objects to sort Books by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BookSort!] - } - - \\"\\"\\" - Fields to sort Books by. The order in which sorts are applied is not guaranteed when specifying many fields in one BookSort object. - \\"\\"\\" - input BookSort { - isbn: SortDirection - originalTitle: SortDirection - } - - union BookTitle = BookTitle_EN | BookTitle_SV - - type BookTitleEnsConnection { - edges: [BookTitle_ENEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type BookTitleSvsConnection { - edges: [BookTitle_SVEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input BookTitleWhere { - BookTitle_EN: BookTitle_ENWhere - BookTitle_SV: BookTitle_SVWhere - } - - type BookTitle_EN { - book(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: BookOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookSort!], where: BookWhere): Book! - bookAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: BookWhere): BookTitle_ENBookBookAggregationSelection - bookConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [BookTitle_ENBookConnectionSort!], where: BookTitle_ENBookConnectionWhere): BookTitle_ENBookConnection! - value: String! - } - - type BookTitle_ENAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - input BookTitle_ENBookAggregateInput { - AND: [BookTitle_ENBookAggregateInput!] - NOT: BookTitle_ENBookAggregateInput - OR: [BookTitle_ENBookAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: BookTitle_ENBookNodeAggregationWhereInput - } - - type BookTitle_ENBookBookAggregationSelection { - count: Int! - node: BookTitle_ENBookBookNodeAggregateSelection - } - - type BookTitle_ENBookBookNodeAggregateSelection { - isbn: StringAggregateSelection! - originalTitle: StringAggregateSelection! - } - - input BookTitle_ENBookConnectFieldInput { - connect: BookConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: BookConnectWhere - } - - type BookTitle_ENBookConnection { - edges: [BookTitle_ENBookRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input BookTitle_ENBookConnectionSort { - node: BookSort - } - - input BookTitle_ENBookConnectionWhere { - AND: [BookTitle_ENBookConnectionWhere!] - NOT: BookTitle_ENBookConnectionWhere - OR: [BookTitle_ENBookConnectionWhere!] - node: BookWhere - } - - input BookTitle_ENBookCreateFieldInput { - node: BookCreateInput! - } - - input BookTitle_ENBookDeleteFieldInput { - delete: BookDeleteInput - where: BookTitle_ENBookConnectionWhere - } - - input BookTitle_ENBookDisconnectFieldInput { - disconnect: BookDisconnectInput - where: BookTitle_ENBookConnectionWhere - } - - input BookTitle_ENBookFieldInput { - connect: BookTitle_ENBookConnectFieldInput - create: BookTitle_ENBookCreateFieldInput - } - - input BookTitle_ENBookNodeAggregationWhereInput { - AND: [BookTitle_ENBookNodeAggregationWhereInput!] - NOT: BookTitle_ENBookNodeAggregationWhereInput - OR: [BookTitle_ENBookNodeAggregationWhereInput!] - isbn_AVERAGE_LENGTH_EQUAL: Float - isbn_AVERAGE_LENGTH_GT: Float - isbn_AVERAGE_LENGTH_GTE: Float - isbn_AVERAGE_LENGTH_LT: Float - isbn_AVERAGE_LENGTH_LTE: Float - isbn_LONGEST_LENGTH_EQUAL: Int - isbn_LONGEST_LENGTH_GT: Int - isbn_LONGEST_LENGTH_GTE: Int - isbn_LONGEST_LENGTH_LT: Int - isbn_LONGEST_LENGTH_LTE: Int - isbn_SHORTEST_LENGTH_EQUAL: Int - isbn_SHORTEST_LENGTH_GT: Int - isbn_SHORTEST_LENGTH_GTE: Int - isbn_SHORTEST_LENGTH_LT: Int - isbn_SHORTEST_LENGTH_LTE: Int - originalTitle_AVERAGE_LENGTH_EQUAL: Float - originalTitle_AVERAGE_LENGTH_GT: Float - originalTitle_AVERAGE_LENGTH_GTE: Float - originalTitle_AVERAGE_LENGTH_LT: Float - originalTitle_AVERAGE_LENGTH_LTE: Float - originalTitle_LONGEST_LENGTH_EQUAL: Int - originalTitle_LONGEST_LENGTH_GT: Int - originalTitle_LONGEST_LENGTH_GTE: Int - originalTitle_LONGEST_LENGTH_LT: Int - originalTitle_LONGEST_LENGTH_LTE: Int - originalTitle_SHORTEST_LENGTH_EQUAL: Int - originalTitle_SHORTEST_LENGTH_GT: Int - originalTitle_SHORTEST_LENGTH_GTE: Int - originalTitle_SHORTEST_LENGTH_LT: Int - originalTitle_SHORTEST_LENGTH_LTE: Int - } - - type BookTitle_ENBookRelationship { - cursor: String! - node: Book! - } - - input BookTitle_ENBookUpdateConnectionInput { - node: BookUpdateInput - } - - input BookTitle_ENBookUpdateFieldInput { - connect: BookTitle_ENBookConnectFieldInput - create: BookTitle_ENBookCreateFieldInput - delete: BookTitle_ENBookDeleteFieldInput - disconnect: BookTitle_ENBookDisconnectFieldInput - update: BookTitle_ENBookUpdateConnectionInput - where: BookTitle_ENBookConnectionWhere - } - - input BookTitle_ENConnectInput { - book: BookTitle_ENBookConnectFieldInput - } - - input BookTitle_ENConnectWhere { - node: BookTitle_ENWhere! - } - - input BookTitle_ENCreateInput { - book: BookTitle_ENBookFieldInput - value: String! - } - - input BookTitle_ENDeleteInput { - book: BookTitle_ENBookDeleteFieldInput - } - - input BookTitle_ENDisconnectInput { - book: BookTitle_ENBookDisconnectFieldInput - } - - type BookTitle_ENEdge { - cursor: String! - node: BookTitle_EN! - } - - input BookTitle_ENOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BookTitle_ENSort objects to sort BookTitleEns by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BookTitle_ENSort!] - } - - \\"\\"\\" - Fields to sort BookTitleEns by. The order in which sorts are applied is not guaranteed when specifying many fields in one BookTitle_ENSort object. - \\"\\"\\" - input BookTitle_ENSort { - value: SortDirection - } - - input BookTitle_ENUpdateInput { - book: BookTitle_ENBookUpdateFieldInput - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input BookTitle_ENWhere { - AND: [BookTitle_ENWhere!] - NOT: BookTitle_ENWhere - OR: [BookTitle_ENWhere!] - book: BookWhere - bookAggregate: BookTitle_ENBookAggregateInput - bookConnection: BookTitle_ENBookConnectionWhere - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String!] - value_STARTS_WITH: String - } - - type BookTitle_SV { - book(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: BookOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookSort!], where: BookWhere): Book! - bookAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: BookWhere): BookTitle_SVBookBookAggregationSelection - bookConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [BookTitle_SVBookConnectionSort!], where: BookTitle_SVBookConnectionWhere): BookTitle_SVBookConnection! - value: String! - } - - type BookTitle_SVAggregateSelection { - count: Int! - value: StringAggregateSelection! - } - - input BookTitle_SVBookAggregateInput { - AND: [BookTitle_SVBookAggregateInput!] - NOT: BookTitle_SVBookAggregateInput - OR: [BookTitle_SVBookAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: BookTitle_SVBookNodeAggregationWhereInput - } - - type BookTitle_SVBookBookAggregationSelection { - count: Int! - node: BookTitle_SVBookBookNodeAggregateSelection - } - - type BookTitle_SVBookBookNodeAggregateSelection { - isbn: StringAggregateSelection! - originalTitle: StringAggregateSelection! - } - - input BookTitle_SVBookConnectFieldInput { - connect: BookConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: BookConnectWhere - } - - type BookTitle_SVBookConnection { - edges: [BookTitle_SVBookRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input BookTitle_SVBookConnectionSort { - node: BookSort - } - - input BookTitle_SVBookConnectionWhere { - AND: [BookTitle_SVBookConnectionWhere!] - NOT: BookTitle_SVBookConnectionWhere - OR: [BookTitle_SVBookConnectionWhere!] - node: BookWhere - } - - input BookTitle_SVBookCreateFieldInput { - node: BookCreateInput! - } - - input BookTitle_SVBookDeleteFieldInput { - delete: BookDeleteInput - where: BookTitle_SVBookConnectionWhere - } - - input BookTitle_SVBookDisconnectFieldInput { - disconnect: BookDisconnectInput - where: BookTitle_SVBookConnectionWhere - } - - input BookTitle_SVBookFieldInput { - connect: BookTitle_SVBookConnectFieldInput - create: BookTitle_SVBookCreateFieldInput - } - - input BookTitle_SVBookNodeAggregationWhereInput { - AND: [BookTitle_SVBookNodeAggregationWhereInput!] - NOT: BookTitle_SVBookNodeAggregationWhereInput - OR: [BookTitle_SVBookNodeAggregationWhereInput!] - isbn_AVERAGE_LENGTH_EQUAL: Float - isbn_AVERAGE_LENGTH_GT: Float - isbn_AVERAGE_LENGTH_GTE: Float - isbn_AVERAGE_LENGTH_LT: Float - isbn_AVERAGE_LENGTH_LTE: Float - isbn_LONGEST_LENGTH_EQUAL: Int - isbn_LONGEST_LENGTH_GT: Int - isbn_LONGEST_LENGTH_GTE: Int - isbn_LONGEST_LENGTH_LT: Int - isbn_LONGEST_LENGTH_LTE: Int - isbn_SHORTEST_LENGTH_EQUAL: Int - isbn_SHORTEST_LENGTH_GT: Int - isbn_SHORTEST_LENGTH_GTE: Int - isbn_SHORTEST_LENGTH_LT: Int - isbn_SHORTEST_LENGTH_LTE: Int - originalTitle_AVERAGE_LENGTH_EQUAL: Float - originalTitle_AVERAGE_LENGTH_GT: Float - originalTitle_AVERAGE_LENGTH_GTE: Float - originalTitle_AVERAGE_LENGTH_LT: Float - originalTitle_AVERAGE_LENGTH_LTE: Float - originalTitle_LONGEST_LENGTH_EQUAL: Int - originalTitle_LONGEST_LENGTH_GT: Int - originalTitle_LONGEST_LENGTH_GTE: Int - originalTitle_LONGEST_LENGTH_LT: Int - originalTitle_LONGEST_LENGTH_LTE: Int - originalTitle_SHORTEST_LENGTH_EQUAL: Int - originalTitle_SHORTEST_LENGTH_GT: Int - originalTitle_SHORTEST_LENGTH_GTE: Int - originalTitle_SHORTEST_LENGTH_LT: Int - originalTitle_SHORTEST_LENGTH_LTE: Int - } - - type BookTitle_SVBookRelationship { - cursor: String! - node: Book! - } - - input BookTitle_SVBookUpdateConnectionInput { - node: BookUpdateInput - } - - input BookTitle_SVBookUpdateFieldInput { - connect: BookTitle_SVBookConnectFieldInput - create: BookTitle_SVBookCreateFieldInput - delete: BookTitle_SVBookDeleteFieldInput - disconnect: BookTitle_SVBookDisconnectFieldInput - update: BookTitle_SVBookUpdateConnectionInput - where: BookTitle_SVBookConnectionWhere - } - - input BookTitle_SVConnectInput { - book: BookTitle_SVBookConnectFieldInput - } - - input BookTitle_SVConnectWhere { - node: BookTitle_SVWhere! - } - - input BookTitle_SVCreateInput { - book: BookTitle_SVBookFieldInput - value: String! - } - - input BookTitle_SVDeleteInput { - book: BookTitle_SVBookDeleteFieldInput - } - - input BookTitle_SVDisconnectInput { - book: BookTitle_SVBookDisconnectFieldInput - } - - type BookTitle_SVEdge { - cursor: String! - node: BookTitle_SV! - } - - input BookTitle_SVOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more BookTitle_SVSort objects to sort BookTitleSvs by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [BookTitle_SVSort!] - } - - \\"\\"\\" - Fields to sort BookTitleSvs by. The order in which sorts are applied is not guaranteed when specifying many fields in one BookTitle_SVSort object. - \\"\\"\\" - input BookTitle_SVSort { - value: SortDirection - } - - input BookTitle_SVUpdateInput { - book: BookTitle_SVBookUpdateFieldInput - value: String @deprecated(reason: \\"Please use the explicit _SET field\\") - value_SET: String - } - - input BookTitle_SVWhere { - AND: [BookTitle_SVWhere!] - NOT: BookTitle_SVWhere - OR: [BookTitle_SVWhere!] - book: BookWhere - bookAggregate: BookTitle_SVBookAggregateInput - bookConnection: BookTitle_SVBookConnectionWhere - value: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - value_CONTAINS: String - value_ENDS_WITH: String - value_EQ: String - value_IN: [String!] - value_STARTS_WITH: String - } - - input BookTranslatedTitleBookTitle_ENConnectFieldInput { - connect: BookTitle_ENConnectInput - where: BookTitle_ENConnectWhere - } - - input BookTranslatedTitleBookTitle_ENConnectionWhere { - AND: [BookTranslatedTitleBookTitle_ENConnectionWhere!] - NOT: BookTranslatedTitleBookTitle_ENConnectionWhere - OR: [BookTranslatedTitleBookTitle_ENConnectionWhere!] - node: BookTitle_ENWhere - } - - input BookTranslatedTitleBookTitle_ENCreateFieldInput { - node: BookTitle_ENCreateInput! - } - - input BookTranslatedTitleBookTitle_ENDeleteFieldInput { - delete: BookTitle_ENDeleteInput - where: BookTranslatedTitleBookTitle_ENConnectionWhere - } - - input BookTranslatedTitleBookTitle_ENDisconnectFieldInput { - disconnect: BookTitle_ENDisconnectInput - where: BookTranslatedTitleBookTitle_ENConnectionWhere - } - - input BookTranslatedTitleBookTitle_ENFieldInput { - connect: BookTranslatedTitleBookTitle_ENConnectFieldInput - create: BookTranslatedTitleBookTitle_ENCreateFieldInput - } - - input BookTranslatedTitleBookTitle_ENUpdateConnectionInput { - node: BookTitle_ENUpdateInput - } - - input BookTranslatedTitleBookTitle_ENUpdateFieldInput { - connect: BookTranslatedTitleBookTitle_ENConnectFieldInput - create: BookTranslatedTitleBookTitle_ENCreateFieldInput - delete: BookTranslatedTitleBookTitle_ENDeleteFieldInput - disconnect: BookTranslatedTitleBookTitle_ENDisconnectFieldInput - update: BookTranslatedTitleBookTitle_ENUpdateConnectionInput - where: BookTranslatedTitleBookTitle_ENConnectionWhere - } - - input BookTranslatedTitleBookTitle_SVConnectFieldInput { - connect: BookTitle_SVConnectInput - where: BookTitle_SVConnectWhere - } - - input BookTranslatedTitleBookTitle_SVConnectionWhere { - AND: [BookTranslatedTitleBookTitle_SVConnectionWhere!] - NOT: BookTranslatedTitleBookTitle_SVConnectionWhere - OR: [BookTranslatedTitleBookTitle_SVConnectionWhere!] - node: BookTitle_SVWhere - } - - input BookTranslatedTitleBookTitle_SVCreateFieldInput { - node: BookTitle_SVCreateInput! - } - - input BookTranslatedTitleBookTitle_SVDeleteFieldInput { - delete: BookTitle_SVDeleteInput - where: BookTranslatedTitleBookTitle_SVConnectionWhere - } - - input BookTranslatedTitleBookTitle_SVDisconnectFieldInput { - disconnect: BookTitle_SVDisconnectInput - where: BookTranslatedTitleBookTitle_SVConnectionWhere - } - - input BookTranslatedTitleBookTitle_SVFieldInput { - connect: BookTranslatedTitleBookTitle_SVConnectFieldInput - create: BookTranslatedTitleBookTitle_SVCreateFieldInput - } - - input BookTranslatedTitleBookTitle_SVUpdateConnectionInput { - node: BookTitle_SVUpdateInput - } - - input BookTranslatedTitleBookTitle_SVUpdateFieldInput { - connect: BookTranslatedTitleBookTitle_SVConnectFieldInput - create: BookTranslatedTitleBookTitle_SVCreateFieldInput - delete: BookTranslatedTitleBookTitle_SVDeleteFieldInput - disconnect: BookTranslatedTitleBookTitle_SVDisconnectFieldInput - update: BookTranslatedTitleBookTitle_SVUpdateConnectionInput - where: BookTranslatedTitleBookTitle_SVConnectionWhere - } - - input BookTranslatedTitleConnectInput { - BookTitle_EN: BookTranslatedTitleBookTitle_ENConnectFieldInput - BookTitle_SV: BookTranslatedTitleBookTitle_SVConnectFieldInput - } - - type BookTranslatedTitleConnection { - edges: [BookTranslatedTitleRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input BookTranslatedTitleConnectionWhere { - BookTitle_EN: BookTranslatedTitleBookTitle_ENConnectionWhere - BookTitle_SV: BookTranslatedTitleBookTitle_SVConnectionWhere - } - - input BookTranslatedTitleCreateInput { - BookTitle_EN: BookTranslatedTitleBookTitle_ENFieldInput - BookTitle_SV: BookTranslatedTitleBookTitle_SVFieldInput - } - - input BookTranslatedTitleDeleteInput { - BookTitle_EN: BookTranslatedTitleBookTitle_ENDeleteFieldInput - BookTitle_SV: BookTranslatedTitleBookTitle_SVDeleteFieldInput - } - - input BookTranslatedTitleDisconnectInput { - BookTitle_EN: BookTranslatedTitleBookTitle_ENDisconnectFieldInput - BookTitle_SV: BookTranslatedTitleBookTitle_SVDisconnectFieldInput - } - - type BookTranslatedTitleRelationship { - cursor: String! - node: BookTitle! - } - - input BookTranslatedTitleUpdateInput { - BookTitle_EN: BookTranslatedTitleBookTitle_ENUpdateFieldInput - BookTitle_SV: BookTranslatedTitleBookTitle_SVUpdateFieldInput - } - - input BookUpdateInput { - isbn: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isbn_SET: String - originalTitle: String @deprecated(reason: \\"Please use the explicit _SET field\\") - originalTitle_SET: String - translatedTitle: BookTranslatedTitleUpdateInput - } - - input BookWhere { - AND: [BookWhere!] - NOT: BookWhere - OR: [BookWhere!] - isbn: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isbn_CONTAINS: String - isbn_ENDS_WITH: String - isbn_EQ: String - isbn_IN: [String!] - isbn_STARTS_WITH: String - originalTitle: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - originalTitle_CONTAINS: String - originalTitle_ENDS_WITH: String - originalTitle_EQ: String - originalTitle_IN: [String!] - originalTitle_STARTS_WITH: String - translatedTitle: BookTitleWhere - translatedTitleConnection: BookTranslatedTitleConnectionWhere - } - - type BooksConnection { - edges: [BookEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateBookTitleEnsMutationResponse { - bookTitleEns: [BookTitle_EN!]! - info: CreateInfo! - } - - type CreateBookTitleSvsMutationResponse { - bookTitleSvs: [BookTitle_SV!]! - info: CreateInfo! - } - - type CreateBooksMutationResponse { - books: [Book!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createBookTitleEns(input: [BookTitle_ENCreateInput!]!): CreateBookTitleEnsMutationResponse! - createBookTitleSvs(input: [BookTitle_SVCreateInput!]!): CreateBookTitleSvsMutationResponse! - createBooks(input: [BookCreateInput!]!): CreateBooksMutationResponse! - deleteBookTitleEns(delete: BookTitle_ENDeleteInput, where: BookTitle_ENWhere): DeleteInfo! - deleteBookTitleSvs(delete: BookTitle_SVDeleteInput, where: BookTitle_SVWhere): DeleteInfo! - deleteBooks(delete: BookDeleteInput, where: BookWhere): DeleteInfo! - updateBookTitleEns(update: BookTitle_ENUpdateInput, where: BookTitle_ENWhere): UpdateBookTitleEnsMutationResponse! - updateBookTitleSvs(update: BookTitle_SVUpdateInput, where: BookTitle_SVWhere): UpdateBookTitleSvsMutationResponse! - updateBooks(update: BookUpdateInput, where: BookWhere): UpdateBooksMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - bookTitleEns(limit: Int, offset: Int, options: BookTitle_ENOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookTitle_ENSort!], where: BookTitle_ENWhere): [BookTitle_EN!]! - bookTitleEnsAggregate(where: BookTitle_ENWhere): BookTitle_ENAggregateSelection! - bookTitleEnsConnection(after: String, first: Int, sort: [BookTitle_ENSort!], where: BookTitle_ENWhere): BookTitleEnsConnection! - bookTitleSvs(limit: Int, offset: Int, options: BookTitle_SVOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookTitle_SVSort!], where: BookTitle_SVWhere): [BookTitle_SV!]! - bookTitleSvsAggregate(where: BookTitle_SVWhere): BookTitle_SVAggregateSelection! - bookTitleSvsConnection(after: String, first: Int, sort: [BookTitle_SVSort!], where: BookTitle_SVWhere): BookTitleSvsConnection! - bookTitles(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: BookTitleWhere): [BookTitle!]! - books(limit: Int, offset: Int, options: BookOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [BookSort!], where: BookWhere): [Book!]! - booksAggregate(where: BookWhere): BookAggregateSelection! - booksConnection(after: String, first: Int, sort: [BookSort!], where: BookWhere): BooksConnection! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateBookTitleEnsMutationResponse { - bookTitleEns: [BookTitle_EN!]! - info: UpdateInfo! - } - - type UpdateBookTitleSvsMutationResponse { - bookTitleSvs: [BookTitle_SV!]! - info: UpdateInfo! - } - - type UpdateBooksMutationResponse { - books: [Book!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/issues/2993.test.ts b/packages/graphql/tests/schema/issues/2993.test.ts index 9a9bd7c0d0..2acc48f191 100644 --- a/packages/graphql/tests/schema/issues/2993.test.ts +++ b/packages/graphql/tests/schema/issues/2993.test.ts @@ -71,6 +71,27 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -91,16 +112,17 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { AND: [FOLLOWSAggregationWhereInput!] NOT: FOLLOWSAggregationWhereInput OR: [FOLLOWSAggregationWhereInput!] - since_MAX_EQUAL: DateTime - since_MAX_GT: DateTime - since_MAX_GTE: DateTime - since_MAX_LT: DateTime - since_MAX_LTE: DateTime - since_MIN_EQUAL: DateTime - since_MIN_GT: DateTime - since_MIN_GTE: DateTime - since_MIN_LT: DateTime - since_MIN_LTE: DateTime + since: DateTimeScalarAggregationFilters + since_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { eq: ... } } }' instead.\\") + since_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { gt: ... } } }' instead.\\") + since_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { gte: ... } } }' instead.\\") + since_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { lt: ... } } }' instead.\\") + since_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { max: { lte: ... } } }' instead.\\") + since_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { eq: ... } } }' instead.\\") + since_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { gt: ... } } }' instead.\\") + since_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { gte: ... } } }' instead.\\") + since_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { lt: ... } } }' instead.\\") + since_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'since: { min: { lte: ... } } }' instead.\\") } input FOLLOWSSort { @@ -108,26 +130,55 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { } input FOLLOWSUpdateInput { - since: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - since_SET: DateTime + since: DateTimeScalarMutations + since_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'since: { set: ... } }' instead.\\") } input FOLLOWSWhere { AND: [FOLLOWSWhere!] NOT: FOLLOWSWhere OR: [FOLLOWSWhere!] - since: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - since_EQ: DateTime - since_GT: DateTime - since_GTE: DateTime - since_IN: [DateTime!] - since_LT: DateTime - since_LTE: DateTime + since: DateTimeScalarFilters + since_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter since: { eq: ... }\\") + since_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter since: { gt: ... }\\") + since_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter since: { gte: ... }\\") + since_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter since: { in: ... }\\") + since_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter since: { lt: ... }\\") + since_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter since: { lte: ... }\\") + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Mutation { @@ -151,7 +202,6 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { type ProfileAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") userName: StringAggregateSelection! } @@ -172,13 +222,15 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { User } - input ProfileOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProfileSort objects to sort Profiles by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProfileSort!] + input ProfileRelationshipFilters { + \\"\\"\\"Filter type where all of the related Profiles match this filter\\"\\"\\" + all: ProfileWhere + \\"\\"\\"Filter type where none of the related Profiles match this filter\\"\\"\\" + none: ProfileWhere + \\"\\"\\"Filter type where one of the related Profiles match this filter\\"\\"\\" + single: ProfileWhere + \\"\\"\\"Filter type where some of the related Profiles match this filter\\"\\"\\" + some: ProfileWhere } \\"\\"\\" @@ -190,30 +242,29 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { } input ProfileUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - userName: String @deprecated(reason: \\"Please use the explicit _SET field\\") - userName_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + userName: StringScalarMutations + userName_SET: String @deprecated(reason: \\"Please use the generic mutation 'userName: { set: ... } }' instead.\\") } input ProfileWhere { AND: [ProfileWhere!] NOT: ProfileWhere OR: [ProfileWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") typename: [ProfileImplementation!] - typename_IN: [ProfileImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - userName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - userName_CONTAINS: String - userName_ENDS_WITH: String - userName_EQ: String - userName_IN: [String!] - userName_STARTS_WITH: String + userName: StringScalarFilters + userName_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter userName: { contains: ... }\\") + userName_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter userName: { endsWith: ... }\\") + userName_EQ: String @deprecated(reason: \\"Please use the relevant generic filter userName: { eq: ... }\\") + userName_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter userName: { in: ... }\\") + userName_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter userName: { startsWith: ... }\\") } type ProfilesConnection { @@ -223,10 +274,10 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { } type Query { - profiles(limit: Int, offset: Int, options: ProfileOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProfileSort!], where: ProfileWhere): [Profile!]! + profiles(limit: Int, offset: Int, sort: [ProfileSort!], where: ProfileWhere): [Profile!]! profilesAggregate(where: ProfileWhere): ProfileAggregateSelection! profilesConnection(after: String, first: Int, sort: [ProfileSort!], where: ProfileWhere): ProfilesConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -244,6 +295,27 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -260,16 +332,15 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { } type User implements Profile { - following(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProfileOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProfileSort!], where: ProfileWhere): [Profile!]! - followingAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProfileWhere): UserProfileFollowingAggregationSelection - followingConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserFollowingConnectionSort!], where: UserFollowingConnectionWhere): UserFollowingConnection! + following(limit: Int, offset: Int, sort: [ProfileSort!], where: ProfileWhere): [Profile!]! + followingAggregate(where: ProfileWhere): UserProfileFollowingAggregationSelection + followingConnection(after: String, first: Int, sort: [UserFollowingConnectionSort!], where: UserFollowingConnectionWhere): UserFollowingConnection! id: ID! userName: String! } type UserAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") userName: StringAggregateSelection! } @@ -291,7 +362,7 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { AND: [UserFollowingAggregateInput!] NOT: UserFollowingAggregateInput OR: [UserFollowingAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -311,6 +382,25 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { totalCount: Int! } + input UserFollowingConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserFollowingConnections match this filter + \\"\\"\\" + all: UserFollowingConnectionWhere + \\"\\"\\" + Return Users where none of the related UserFollowingConnections match this filter + \\"\\"\\" + none: UserFollowingConnectionWhere + \\"\\"\\" + Return Users where one of the related UserFollowingConnections match this filter + \\"\\"\\" + single: UserFollowingConnectionWhere + \\"\\"\\" + Return Users where some of the related UserFollowingConnections match this filter + \\"\\"\\" + some: UserFollowingConnectionWhere + } + input UserFollowingConnectionSort { edge: FOLLOWSSort node: ProfileSort @@ -345,31 +435,22 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { AND: [UserFollowingNodeAggregationWhereInput!] NOT: UserFollowingNodeAggregationWhereInput OR: [UserFollowingNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - userName_AVERAGE_LENGTH_EQUAL: Float - userName_AVERAGE_LENGTH_GT: Float - userName_AVERAGE_LENGTH_GTE: Float - userName_AVERAGE_LENGTH_LT: Float - userName_AVERAGE_LENGTH_LTE: Float - userName_LONGEST_LENGTH_EQUAL: Int - userName_LONGEST_LENGTH_GT: Int - userName_LONGEST_LENGTH_GTE: Int - userName_LONGEST_LENGTH_LT: Int - userName_LONGEST_LENGTH_LTE: Int - userName_SHORTEST_LENGTH_EQUAL: Int - userName_SHORTEST_LENGTH_GT: Int - userName_SHORTEST_LENGTH_GTE: Int - userName_SHORTEST_LENGTH_LT: Int - userName_SHORTEST_LENGTH_LTE: Int + userName: StringScalarAggregationFilters + userName_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'userName: { averageLength: { eq: ... } } }' instead.\\") + userName_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'userName: { averageLength: { gt: ... } } }' instead.\\") + userName_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'userName: { averageLength: { gte: ... } } }' instead.\\") + userName_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'userName: { averageLength: { lt: ... } } }' instead.\\") + userName_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'userName: { averageLength: { lte: ... } } }' instead.\\") + userName_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { longestLength: { eq: ... } } }' instead.\\") + userName_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { longestLength: { gt: ... } } }' instead.\\") + userName_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { longestLength: { gte: ... } } }' instead.\\") + userName_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { longestLength: { lt: ... } } }' instead.\\") + userName_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { longestLength: { lte: ... } } }' instead.\\") + userName_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { shortestLength: { eq: ... } } }' instead.\\") + userName_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { shortestLength: { gt: ... } } }' instead.\\") + userName_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { shortestLength: { gte: ... } } }' instead.\\") + userName_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { shortestLength: { lt: ... } } }' instead.\\") + userName_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'userName: { shortestLength: { lte: ... } } }' instead.\\") } type UserFollowingRelationship { @@ -392,15 +473,6 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { where: UserFollowingConnectionWhere } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - type UserProfileFollowingAggregationSelection { count: Int! edge: UserProfileFollowingEdgeAggregateSelection @@ -412,7 +484,6 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { } type UserProfileFollowingNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") userName: StringAggregateSelection! } @@ -426,51 +497,53 @@ describe("https://github.com/neo4j/graphql/issues/2993", () => { input UserUpdateInput { following: [UserFollowingUpdateFieldInput!] - userName: String @deprecated(reason: \\"Please use the explicit _SET field\\") - userName_SET: String + userName: StringScalarMutations + userName_SET: String @deprecated(reason: \\"Please use the generic mutation 'userName: { set: ... } }' instead.\\") } input UserWhere { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] + following: ProfileRelationshipFilters followingAggregate: UserFollowingAggregateInput + followingConnection: UserFollowingConnectionFilters \\"\\"\\" Return Users where all of the related UserFollowingConnections match this filter \\"\\"\\" - followingConnection_ALL: UserFollowingConnectionWhere + followingConnection_ALL: UserFollowingConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'followingConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserFollowingConnections match this filter \\"\\"\\" - followingConnection_NONE: UserFollowingConnectionWhere + followingConnection_NONE: UserFollowingConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'followingConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserFollowingConnections match this filter \\"\\"\\" - followingConnection_SINGLE: UserFollowingConnectionWhere + followingConnection_SINGLE: UserFollowingConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'followingConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserFollowingConnections match this filter \\"\\"\\" - followingConnection_SOME: UserFollowingConnectionWhere + followingConnection_SOME: UserFollowingConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'followingConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Profiles match this filter\\"\\"\\" - following_ALL: ProfileWhere + following_ALL: ProfileWhere @deprecated(reason: \\"Please use the relevant generic filter 'following: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Profiles match this filter\\"\\"\\" - following_NONE: ProfileWhere + following_NONE: ProfileWhere @deprecated(reason: \\"Please use the relevant generic filter 'following: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Profiles match this filter\\"\\"\\" - following_SINGLE: ProfileWhere + following_SINGLE: ProfileWhere @deprecated(reason: \\"Please use the relevant generic filter 'following: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Profiles match this filter\\"\\"\\" - following_SOME: ProfileWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - userName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - userName_CONTAINS: String - userName_ENDS_WITH: String - userName_EQ: String - userName_IN: [String!] - userName_STARTS_WITH: String + following_SOME: ProfileWhere @deprecated(reason: \\"Please use the relevant generic filter 'following: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + userName: StringScalarFilters + userName_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter userName: { contains: ... }\\") + userName_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter userName: { endsWith: ... }\\") + userName_EQ: String @deprecated(reason: \\"Please use the relevant generic filter userName: { eq: ... }\\") + userName_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter userName: { in: ... }\\") + userName_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter userName: { startsWith: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/issues/3428.test.ts b/packages/graphql/tests/schema/issues/3428.test.ts index f337b8317b..3171951bc5 100644 --- a/packages/graphql/tests/schema/issues/3428.test.ts +++ b/packages/graphql/tests/schema/issues/3428.test.ts @@ -26,7 +26,7 @@ describe("Relationship nested operations", () => { test("Single relationship to type with unique field with no nested operation specified", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id name: String } @@ -70,15 +70,44 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -86,7 +115,7 @@ describe("Relationship nested operations", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -101,6 +130,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: PersonSort } @@ -116,31 +164,22 @@ describe("Relationship nested operations", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -150,7 +189,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -162,22 +200,12 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! node: MoviePersonActorsNodeAggregateSelection } type MoviePersonActorsNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -189,45 +217,47 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -266,7 +296,6 @@ describe("Relationship nested operations", () => { type PersonAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -279,13 +308,15 @@ describe("Relationship nested operations", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -297,33 +328,33 @@ describe("Relationship nested operations", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -341,6 +372,27 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -366,11 +418,11 @@ describe("Relationship nested operations", () => { test("Single relationship to union with unique fields with no nested operation specified", async () => { const typeDefs = gql` type PersonOne @node { - name: String @unique + name: String } type PersonTwo @node { - nameTwo: String @unique + nameTwo: String } union Person = PersonOne | PersonTwo @@ -420,14 +472,23 @@ describe("Relationship nested operations", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: PersonWhere): [Person!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID } @@ -437,6 +498,25 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { PersonOne: MovieActorsPersonOneConnectionWhere PersonTwo: MovieActorsPersonTwoConnectionWhere @@ -463,7 +543,6 @@ describe("Relationship nested operations", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -475,15 +554,6 @@ describe("Relationship nested operations", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -492,44 +562,46 @@ describe("Relationship nested operations", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -578,15 +650,6 @@ describe("Relationship nested operations", () => { node: PersonOne! } - input PersonOneOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonOneSort objects to sort PersonOnes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonOneSort!] - } - \\"\\"\\" Fields to sort PersonOnes by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonOneSort object. \\"\\"\\" @@ -595,20 +658,20 @@ describe("Relationship nested operations", () => { } input PersonOneUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonOneWhere { AND: [PersonOneWhere!] NOT: PersonOneWhere OR: [PersonOneWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type PersonOnesConnection { @@ -617,6 +680,17 @@ describe("Relationship nested operations", () => { totalCount: Int! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + type PersonTwo { nameTwo: String } @@ -635,15 +709,6 @@ describe("Relationship nested operations", () => { node: PersonTwo! } - input PersonTwoOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonTwoSort objects to sort PersonTwos by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonTwoSort!] - } - \\"\\"\\" Fields to sort PersonTwos by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonTwoSort object. \\"\\"\\" @@ -652,20 +717,20 @@ describe("Relationship nested operations", () => { } input PersonTwoUpdateInput { - nameTwo: String @deprecated(reason: \\"Please use the explicit _SET field\\") - nameTwo_SET: String + nameTwo: StringScalarMutations + nameTwo_SET: String @deprecated(reason: \\"Please use the generic mutation 'nameTwo: { set: ... } }' instead.\\") } input PersonTwoWhere { AND: [PersonTwoWhere!] NOT: PersonTwoWhere OR: [PersonTwoWhere!] - nameTwo: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - nameTwo_CONTAINS: String - nameTwo_ENDS_WITH: String - nameTwo_EQ: String - nameTwo_IN: [String] - nameTwo_STARTS_WITH: String + nameTwo: StringScalarFilters + nameTwo_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { contains: ... }\\") + nameTwo_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { endsWith: ... }\\") + nameTwo_EQ: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { eq: ... }\\") + nameTwo_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { in: ... }\\") + nameTwo_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter nameTwo: { startsWith: ... }\\") } type PersonTwosConnection { @@ -680,24 +745,18 @@ describe("Relationship nested operations", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - personOnes(limit: Int, offset: Int, options: PersonOneOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! + personOnes(limit: Int, offset: Int, sort: [PersonOneSort!], where: PersonOneWhere): [PersonOne!]! personOnesAggregate(where: PersonOneWhere): PersonOneAggregateSelection! personOnesConnection(after: String, first: Int, sort: [PersonOneSort!], where: PersonOneWhere): PersonOnesConnection! - personTwos(limit: Int, offset: Int, options: PersonTwoOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! + personTwos(limit: Int, offset: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): [PersonTwo!]! personTwosAggregate(where: PersonTwoWhere): PersonTwoAggregateSelection! personTwosConnection(after: String, first: Int, sort: [PersonTwoSort!], where: PersonTwoWhere): PersonTwosConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -711,6 +770,20 @@ describe("Relationship nested operations", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/issues/3439.test.ts b/packages/graphql/tests/schema/issues/3439.test.ts deleted file mode 100644 index e382ec6dac..0000000000 --- a/packages/graphql/tests/schema/issues/3439.test.ts +++ /dev/null @@ -1,4951 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { validateSchema } from "graphql"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; -import { TestCDCEngine } from "../../utils/builders/TestCDCEngine"; - -describe("https://github.com/neo4j/graphql/issues/3439", () => { - test("Type definitions implementing multiple interfaces", async () => { - const typeDefs = gql` - interface INode { - id: String! - } - - interface IProduct implements INode { - id: String! - - name: String! - genre: Genre! - } - - type Movie implements INode & IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Series implements INode & IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface INode { - id: String! - } - - type INodeAggregateSelection { - count: Int! - id: StringAggregateSelection! - } - - type INodeEdge { - cursor: String! - node: INode! - } - - enum INodeImplementation { - Movie - Series - } - - input INodeOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more INodeSort objects to sort INodes by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [INodeSort!] - } - - \\"\\"\\" - Fields to sort INodes by. The order in which sorts are applied is not guaranteed when specifying many fields in one INodeSort object. - \\"\\"\\" - input INodeSort { - id: SortDirection - } - - input INodeWhere { - AND: [INodeWhere!] - NOT: INodeWhere - OR: [INodeWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - typename: [INodeImplementation!] - typename_IN: [INodeImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type INodesConnection { - edges: [INodeEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre: Genre! - id: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - enum IProductImplementation { - Movie - Series - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie implements INode & IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! - id: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: MovieGenreDeleteFieldInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type MovieGenreConnection { - edges: [MovieGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieGenreConnectionSort { - node: GenreSort - } - - input MovieGenreConnectionWhere { - AND: [MovieGenreConnectionWhere!] - NOT: MovieGenreConnectionWhere - OR: [MovieGenreConnectionWhere!] - node: GenreWhere - } - - input MovieGenreCreateFieldInput { - node: GenreCreateInput! - } - - input MovieGenreDeleteFieldInput { - delete: GenreDeleteInput - where: MovieGenreConnectionWhere - } - - input MovieGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: MovieGenreConnectionWhere - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieGenreRelationship { - cursor: String! - node: Genre! - } - - input MovieGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: MovieGenreDeleteFieldInput - disconnect: MovieGenreDisconnectFieldInput - update: MovieGenreUpdateConnectionInput - where: MovieGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: MovieGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iNodes(limit: Int, offset: Int, options: INodeOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [INodeSort!], where: INodeWhere): [INode!]! - iNodesAggregate(where: INodeWhere): INodeAggregateSelection! - iNodesConnection(after: String, first: Int, sort: [INodeSort!], where: INodeWhere): INodesConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements INode & IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): SeriesGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesGenreConnectionSort!], where: SeriesGenreConnectionWhere): SeriesGenreConnection! - id: String! - name: String! - } - - type SeriesAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - genre: SeriesGenreFieldInput - id: String! - name: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDeleteInput { - genre: SeriesGenreDeleteFieldInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - id: String! - name: String! - } - - input SeriesGenreAggregateInput { - AND: [SeriesGenreAggregateInput!] - NOT: SeriesGenreAggregateInput - OR: [SeriesGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: SeriesGenreNodeAggregationWhereInput - } - - input SeriesGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type SeriesGenreConnection { - edges: [SeriesGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesGenreConnectionSort { - node: GenreSort - } - - input SeriesGenreConnectionWhere { - AND: [SeriesGenreConnectionWhere!] - NOT: SeriesGenreConnectionWhere - OR: [SeriesGenreConnectionWhere!] - node: GenreWhere - } - - input SeriesGenreCreateFieldInput { - node: GenreCreateInput! - } - - input SeriesGenreDeleteFieldInput { - delete: GenreDeleteInput - where: SeriesGenreConnectionWhere - } - - input SeriesGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: SeriesGenreConnectionWhere - } - - input SeriesGenreFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreCreateFieldInput - } - - type SeriesGenreGenreAggregationSelection { - count: Int! - node: SeriesGenreGenreNodeAggregateSelection - } - - type SeriesGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input SeriesGenreNodeAggregationWhereInput { - AND: [SeriesGenreNodeAggregationWhereInput!] - NOT: SeriesGenreNodeAggregationWhereInput - OR: [SeriesGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type SeriesGenreRelationship { - cursor: String! - node: Genre! - } - - input SeriesGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input SeriesGenreUpdateFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput - create: SeriesGenreCreateFieldInput - delete: SeriesGenreDeleteFieldInput - disconnect: SeriesGenreDisconnectFieldInput - update: SeriesGenreUpdateConnectionInput - where: SeriesGenreConnectionWhere - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - name: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input SeriesUpdateInput { - genre: SeriesGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - genre: GenreWhere - genreAggregate: SeriesGenreAggregateInput - genreConnection: SeriesGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); - - test("Simple type definitions implementing just one interface", async () => { - const typeDefs = gql` - interface IProduct { - id: String! - - name: String! - genre: Genre! - } - - type Movie implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Series implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre: Genre! - id: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - enum IProductImplementation { - Movie - Series - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! - id: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: MovieGenreDeleteFieldInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type MovieGenreConnection { - edges: [MovieGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieGenreConnectionSort { - node: GenreSort - } - - input MovieGenreConnectionWhere { - AND: [MovieGenreConnectionWhere!] - NOT: MovieGenreConnectionWhere - OR: [MovieGenreConnectionWhere!] - node: GenreWhere - } - - input MovieGenreCreateFieldInput { - node: GenreCreateInput! - } - - input MovieGenreDeleteFieldInput { - delete: GenreDeleteInput - where: MovieGenreConnectionWhere - } - - input MovieGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: MovieGenreConnectionWhere - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieGenreRelationship { - cursor: String! - node: Genre! - } - - input MovieGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: MovieGenreDeleteFieldInput - disconnect: MovieGenreDisconnectFieldInput - update: MovieGenreUpdateConnectionInput - where: MovieGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: MovieGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): SeriesGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [SeriesGenreConnectionSort!], where: SeriesGenreConnectionWhere): SeriesGenreConnection! - id: String! - name: String! - } - - type SeriesAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - genre: SeriesGenreFieldInput - id: String! - name: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDeleteInput { - genre: SeriesGenreDeleteFieldInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - id: String! - name: String! - } - - input SeriesGenreAggregateInput { - AND: [SeriesGenreAggregateInput!] - NOT: SeriesGenreAggregateInput - OR: [SeriesGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: SeriesGenreNodeAggregationWhereInput - } - - input SeriesGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type SeriesGenreConnection { - edges: [SeriesGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesGenreConnectionSort { - node: GenreSort - } - - input SeriesGenreConnectionWhere { - AND: [SeriesGenreConnectionWhere!] - NOT: SeriesGenreConnectionWhere - OR: [SeriesGenreConnectionWhere!] - node: GenreWhere - } - - input SeriesGenreCreateFieldInput { - node: GenreCreateInput! - } - - input SeriesGenreDeleteFieldInput { - delete: GenreDeleteInput - where: SeriesGenreConnectionWhere - } - - input SeriesGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: SeriesGenreConnectionWhere - } - - input SeriesGenreFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreCreateFieldInput - } - - type SeriesGenreGenreAggregationSelection { - count: Int! - node: SeriesGenreGenreNodeAggregateSelection - } - - type SeriesGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input SeriesGenreNodeAggregationWhereInput { - AND: [SeriesGenreNodeAggregationWhereInput!] - NOT: SeriesGenreNodeAggregationWhereInput - OR: [SeriesGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type SeriesGenreRelationship { - cursor: String! - node: Genre! - } - - input SeriesGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input SeriesGenreUpdateFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput - create: SeriesGenreCreateFieldInput - delete: SeriesGenreDeleteFieldInput - disconnect: SeriesGenreDisconnectFieldInput - update: SeriesGenreUpdateConnectionInput - where: SeriesGenreConnectionWhere - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - name: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input SeriesUpdateInput { - genre: SeriesGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - genre: GenreWhere - genreAggregate: SeriesGenreAggregateInput - genreConnection: SeriesGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); - - test("Simple type definitions implementing just one interface with different relationship properties", async () => { - const typeDefs = gql` - interface IProduct { - id: String! - - name: String! - genre: Genre! @declareRelationship - } - - type Movie implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT, properties: "MovieProps") - } - - type Series implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT, properties: "SeriesProps") - } - - type MovieProps @relationshipProperties { - year: Int! - } - - type SeriesProps @relationshipProperties { - episodes: Int - } - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - connect: IProductConnectInput - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - delete: IProductDeleteInput - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - disconnect: IProductDisconnectInput - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreConnection(after: String, first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectInput { - genre: IProductGenreConnectFieldInput - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - input IProductDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - input IProductDisconnectInput { - genre: IProductGenreDisconnectFieldInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - input IProductGenreAggregateInput { - AND: [IProductGenreAggregateInput!] - NOT: IProductGenreAggregateInput - OR: [IProductGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: IProductGenreEdgeAggregationWhereInput - node: IProductGenreNodeAggregationWhereInput - } - - input IProductGenreConnectFieldInput { - connect: GenreConnectInput - edge: IProductGenreEdgeCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: GenreOnCreateInput! - } - - type IProductGenreConnection { - edges: [IProductGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input IProductGenreConnectionSort { - edge: IProductGenreEdgeSort - node: GenreSort - } - - input IProductGenreConnectionWhere { - AND: [IProductGenreConnectionWhere!] - NOT: IProductGenreConnectionWhere - OR: [IProductGenreConnectionWhere!] - edge: IProductGenreEdgeWhere - node: GenreWhere - } - - input IProductGenreCreateFieldInput { - edge: IProductGenreEdgeCreateInput! - node: GenreCreateInput! - } - - input IProductGenreDeleteFieldInput { - delete: GenreDeleteInput - where: IProductGenreConnectionWhere - } - - input IProductGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: IProductGenreConnectionWhere - } - - input IProductGenreEdgeAggregationWhereInput { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsAggregationWhereInput - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsAggregationWhereInput - } - - input IProductGenreEdgeCreateInput { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsCreateInput! - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsCreateInput - } - - input IProductGenreEdgeSort { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsSort - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsSort - } - - input IProductGenreEdgeUpdateInput { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsUpdateInput - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsUpdateInput - } - - input IProductGenreEdgeWhere { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsWhere - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsWhere - } - - input IProductGenreNodeAggregationWhereInput { - AND: [IProductGenreNodeAggregationWhereInput!] - NOT: IProductGenreNodeAggregationWhereInput - OR: [IProductGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type IProductGenreRelationship { - cursor: String! - node: Genre! - properties: IProductGenreRelationshipProperties! - } - - union IProductGenreRelationshipProperties = MovieProps | SeriesProps - - input IProductGenreUpdateConnectionInput { - edge: IProductGenreEdgeUpdateInput - node: GenreUpdateInput - } - - input IProductGenreUpdateFieldInput { - connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput - create: IProductGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: IProductGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - enum IProductImplementation { - Movie - Series - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - genre: IProductGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - genre: GenreWhere - genreAggregate: IProductGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: MoviePropsAggregationWhereInput - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - edge: MoviePropsCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: GenreOnCreateInput! - } - - input MovieGenreCreateFieldInput { - edge: MoviePropsCreateInput! - node: GenreCreateInput! - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - edge: MovieGenreGenreEdgeAggregateSelection - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreEdgeAggregateSelection { - year: IntAggregateSelection! - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - input MovieGenreUpdateConnectionInput { - edge: MoviePropsUpdateInput - node: GenreUpdateInput - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: MovieGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - The edge properties for the following fields: - * Movie.genre - \\"\\"\\" - type MovieProps { - year: Int! - } - - input MoviePropsAggregationWhereInput { - AND: [MoviePropsAggregationWhereInput!] - NOT: MoviePropsAggregationWhereInput - OR: [MoviePropsAggregationWhereInput!] - year_AVERAGE_EQUAL: Float - year_AVERAGE_GT: Float - year_AVERAGE_GTE: Float - year_AVERAGE_LT: Float - year_AVERAGE_LTE: Float - year_MAX_EQUAL: Int - year_MAX_GT: Int - year_MAX_GTE: Int - year_MAX_LT: Int - year_MAX_LTE: Int - year_MIN_EQUAL: Int - year_MIN_GT: Int - year_MIN_GTE: Int - year_MIN_LT: Int - year_MIN_LTE: Int - year_SUM_EQUAL: Int - year_SUM_GT: Int - year_SUM_GTE: Int - year_SUM_LT: Int - year_SUM_LTE: Int - } - - input MoviePropsCreateInput { - year: Int! - } - - input MoviePropsSort { - year: SortDirection - } - - input MoviePropsUpdateInput { - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int - } - - input MoviePropsWhere { - AND: [MoviePropsWhere!] - NOT: MoviePropsWhere - OR: [MoviePropsWhere!] - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int!] - year_LT: Int - year_LTE: Int - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): SeriesGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type SeriesAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - genre: SeriesGenreFieldInput - id: String! - name: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - id: String! - name: String! - } - - input SeriesGenreAggregateInput { - AND: [SeriesGenreAggregateInput!] - NOT: SeriesGenreAggregateInput - OR: [SeriesGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: SeriesPropsAggregationWhereInput - node: SeriesGenreNodeAggregationWhereInput - } - - input SeriesGenreConnectFieldInput { - connect: GenreConnectInput - edge: SeriesPropsCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: GenreOnCreateInput! - } - - input SeriesGenreCreateFieldInput { - edge: SeriesPropsCreateInput - node: GenreCreateInput! - } - - input SeriesGenreFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreCreateFieldInput - } - - type SeriesGenreGenreAggregationSelection { - count: Int! - edge: SeriesGenreGenreEdgeAggregateSelection - node: SeriesGenreGenreNodeAggregateSelection - } - - type SeriesGenreGenreEdgeAggregateSelection { - episodes: IntAggregateSelection! - } - - type SeriesGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input SeriesGenreNodeAggregationWhereInput { - AND: [SeriesGenreNodeAggregationWhereInput!] - NOT: SeriesGenreNodeAggregationWhereInput - OR: [SeriesGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - input SeriesGenreUpdateConnectionInput { - edge: SeriesPropsUpdateInput - node: GenreUpdateInput - } - - input SeriesGenreUpdateFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput - create: SeriesGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: SeriesGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - The edge properties for the following fields: - * Series.genre - \\"\\"\\" - type SeriesProps { - episodes: Int - } - - input SeriesPropsAggregationWhereInput { - AND: [SeriesPropsAggregationWhereInput!] - NOT: SeriesPropsAggregationWhereInput - OR: [SeriesPropsAggregationWhereInput!] - episodes_AVERAGE_EQUAL: Float - episodes_AVERAGE_GT: Float - episodes_AVERAGE_GTE: Float - episodes_AVERAGE_LT: Float - episodes_AVERAGE_LTE: Float - episodes_MAX_EQUAL: Int - episodes_MAX_GT: Int - episodes_MAX_GTE: Int - episodes_MAX_LT: Int - episodes_MAX_LTE: Int - episodes_MIN_EQUAL: Int - episodes_MIN_GT: Int - episodes_MIN_GTE: Int - episodes_MIN_LT: Int - episodes_MIN_LTE: Int - episodes_SUM_EQUAL: Int - episodes_SUM_GT: Int - episodes_SUM_GTE: Int - episodes_SUM_LT: Int - episodes_SUM_LTE: Int - } - - input SeriesPropsCreateInput { - episodes: Int - } - - input SeriesPropsSort { - episodes: SortDirection - } - - input SeriesPropsUpdateInput { - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - } - - input SeriesPropsWhere { - AND: [SeriesPropsWhere!] - NOT: SeriesPropsWhere - OR: [SeriesPropsWhere!] - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int] - episodes_LT: Int - episodes_LTE: Int - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - name: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input SeriesUpdateInput { - genre: SeriesGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - genre: GenreWhere - genreAggregate: SeriesGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); - - test("Simple type definitions implementing just one interface - relationship to union type", async () => { - const typeDefs = gql` - interface IProduct { - id: String! - - name: String! - genre: UGenre! @declareRelationship - } - - type Movie implements IProduct @node { - id: String! - - name: String! - genre: UGenre! @relationship(type: "HAS_GENRE", direction: OUT, properties: "MovieProps") - } - - type Series implements IProduct @node { - id: String! - - name: String! - genre: UGenre! @relationship(type: "HAS_GENRE", direction: OUT, properties: "SeriesProps") - } - - type MovieProps @relationshipProperties { - year: Int! - } - - type SeriesProps @relationshipProperties { - episodes: Int - } - - union UGenre = Genre | Rating - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - - type Rating @node { - number: Int! @unique - product: [IProduct!]! @relationship(type: "HAS_RATING", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateRatingsMutationResponse { - info: CreateInfo! - ratings: [Rating!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - connect: IProductConnectInput - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - delete: IProductDeleteInput - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - disconnect: IProductDisconnectInput - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: UGenreWhere): UGenre! - genreConnection(after: String, first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectInput { - genre: IProductGenreConnectInput - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - input IProductDeleteInput { - genre: IProductGenreDeleteInput - } - - input IProductDisconnectInput { - genre: IProductGenreDisconnectInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - input IProductGenreConnectInput { - Genre: IProductGenreGenreConnectFieldInput - Rating: IProductGenreRatingConnectFieldInput - } - - type IProductGenreConnection { - edges: [IProductGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input IProductGenreConnectionSort { - edge: IProductGenreEdgeSort - } - - input IProductGenreConnectionWhere { - Genre: IProductGenreGenreConnectionWhere - Rating: IProductGenreRatingConnectionWhere - } - - input IProductGenreDeleteInput { - Genre: IProductGenreGenreDeleteFieldInput - Rating: IProductGenreRatingDeleteFieldInput - } - - input IProductGenreDisconnectInput { - Genre: IProductGenreGenreDisconnectFieldInput - Rating: IProductGenreRatingDisconnectFieldInput - } - - input IProductGenreEdgeCreateInput { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsCreateInput! - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsCreateInput - } - - input IProductGenreEdgeSort { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsSort - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsSort - } - - input IProductGenreEdgeUpdateInput { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsUpdateInput - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsUpdateInput - } - - input IProductGenreEdgeWhere { - \\"\\"\\" - Relationship properties when source node is of type: - * Movie - \\"\\"\\" - MovieProps: MoviePropsWhere - \\"\\"\\" - Relationship properties when source node is of type: - * Series - \\"\\"\\" - SeriesProps: SeriesPropsWhere - } - - input IProductGenreGenreConnectFieldInput { - connect: GenreConnectInput - edge: IProductGenreEdgeCreateInput! - where: GenreConnectWhere - } - - input IProductGenreGenreConnectOrCreateFieldInput { - onCreate: IProductGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreGenreConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: GenreOnCreateInput! - } - - input IProductGenreGenreConnectionWhere { - AND: [IProductGenreGenreConnectionWhere!] - NOT: IProductGenreGenreConnectionWhere - OR: [IProductGenreGenreConnectionWhere!] - edge: IProductGenreEdgeWhere - node: GenreWhere - } - - input IProductGenreGenreCreateFieldInput { - edge: IProductGenreEdgeCreateInput! - node: GenreCreateInput! - } - - input IProductGenreGenreDeleteFieldInput { - delete: GenreDeleteInput - where: IProductGenreGenreConnectionWhere - } - - input IProductGenreGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: IProductGenreGenreConnectionWhere - } - - input IProductGenreGenreUpdateConnectionInput { - edge: IProductGenreEdgeUpdateInput - node: GenreUpdateInput - } - - input IProductGenreGenreUpdateFieldInput { - connect: IProductGenreGenreConnectFieldInput - connectOrCreate: IProductGenreGenreConnectOrCreateFieldInput - create: IProductGenreGenreCreateFieldInput - delete: IProductGenreGenreDeleteFieldInput - disconnect: IProductGenreGenreDisconnectFieldInput - update: IProductGenreGenreUpdateConnectionInput - where: IProductGenreGenreConnectionWhere - } - - input IProductGenreRatingConnectFieldInput { - connect: RatingConnectInput - edge: IProductGenreEdgeCreateInput! - where: RatingConnectWhere - } - - input IProductGenreRatingConnectOrCreateFieldInput { - onCreate: IProductGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input IProductGenreRatingConnectOrCreateFieldInputOnCreate { - edge: IProductGenreEdgeCreateInput! - node: RatingOnCreateInput! - } - - input IProductGenreRatingConnectionWhere { - AND: [IProductGenreRatingConnectionWhere!] - NOT: IProductGenreRatingConnectionWhere - OR: [IProductGenreRatingConnectionWhere!] - edge: IProductGenreEdgeWhere - node: RatingWhere - } - - input IProductGenreRatingCreateFieldInput { - edge: IProductGenreEdgeCreateInput! - node: RatingCreateInput! - } - - input IProductGenreRatingDeleteFieldInput { - delete: RatingDeleteInput - where: IProductGenreRatingConnectionWhere - } - - input IProductGenreRatingDisconnectFieldInput { - disconnect: RatingDisconnectInput - where: IProductGenreRatingConnectionWhere - } - - input IProductGenreRatingUpdateConnectionInput { - edge: IProductGenreEdgeUpdateInput - node: RatingUpdateInput - } - - input IProductGenreRatingUpdateFieldInput { - connect: IProductGenreRatingConnectFieldInput - connectOrCreate: IProductGenreRatingConnectOrCreateFieldInput - create: IProductGenreRatingCreateFieldInput - delete: IProductGenreRatingDeleteFieldInput - disconnect: IProductGenreRatingDisconnectFieldInput - update: IProductGenreRatingUpdateConnectionInput - where: IProductGenreRatingConnectionWhere - } - - type IProductGenreRelationship { - cursor: String! - node: UGenre! - properties: IProductGenreRelationshipProperties! - } - - union IProductGenreRelationshipProperties = MovieProps | SeriesProps - - input IProductGenreUpdateInput { - Genre: IProductGenreGenreUpdateFieldInput - Rating: IProductGenreRatingUpdateFieldInput - } - - enum IProductImplementation { - Movie - Series - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - genre: IProductGenreUpdateInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - genre: UGenreWhere - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: UGenreWhere): UGenre! - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreCreateInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: MovieGenreDeleteInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreCreateInput { - Genre: MovieGenreGenreFieldInput - Rating: MovieGenreRatingFieldInput - } - - input MovieGenreDeleteInput { - Genre: IProductGenreGenreDeleteFieldInput - Rating: IProductGenreRatingDeleteFieldInput - } - - input MovieGenreGenreConnectFieldInput { - connect: GenreConnectInput - edge: MoviePropsCreateInput! - where: GenreConnectWhere - } - - input MovieGenreGenreConnectOrCreateFieldInput { - onCreate: MovieGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreGenreConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: GenreOnCreateInput! - } - - input MovieGenreGenreCreateFieldInput { - edge: MoviePropsCreateInput! - node: GenreCreateInput! - } - - input MovieGenreGenreFieldInput { - connect: MovieGenreGenreConnectFieldInput - connectOrCreate: MovieGenreGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreGenreCreateFieldInput - } - - input MovieGenreGenreUpdateConnectionInput { - edge: MoviePropsUpdateInput - node: GenreUpdateInput - } - - input MovieGenreGenreUpdateFieldInput { - connect: MovieGenreGenreConnectFieldInput - connectOrCreate: MovieGenreGenreConnectOrCreateFieldInput - create: MovieGenreGenreCreateFieldInput - delete: IProductGenreGenreDeleteFieldInput - disconnect: IProductGenreGenreDisconnectFieldInput - update: MovieGenreGenreUpdateConnectionInput - where: IProductGenreGenreConnectionWhere - } - - input MovieGenreRatingConnectFieldInput { - connect: RatingConnectInput - edge: MoviePropsCreateInput! - where: RatingConnectWhere - } - - input MovieGenreRatingConnectOrCreateFieldInput { - onCreate: MovieGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input MovieGenreRatingConnectOrCreateFieldInputOnCreate { - edge: MoviePropsCreateInput! - node: RatingOnCreateInput! - } - - input MovieGenreRatingCreateFieldInput { - edge: MoviePropsCreateInput! - node: RatingCreateInput! - } - - input MovieGenreRatingFieldInput { - connect: MovieGenreRatingConnectFieldInput - connectOrCreate: MovieGenreRatingConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreRatingCreateFieldInput - } - - input MovieGenreRatingUpdateConnectionInput { - edge: MoviePropsUpdateInput - node: RatingUpdateInput - } - - input MovieGenreRatingUpdateFieldInput { - connect: MovieGenreRatingConnectFieldInput - connectOrCreate: MovieGenreRatingConnectOrCreateFieldInput - create: MovieGenreRatingCreateFieldInput - delete: IProductGenreRatingDeleteFieldInput - disconnect: IProductGenreRatingDisconnectFieldInput - update: MovieGenreRatingUpdateConnectionInput - where: IProductGenreRatingConnectionWhere - } - - input MovieGenreUpdateInput { - Genre: MovieGenreGenreUpdateFieldInput - Rating: MovieGenreRatingUpdateFieldInput - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - The edge properties for the following fields: - * Movie.genre - \\"\\"\\" - type MovieProps { - year: Int! - } - - input MoviePropsCreateInput { - year: Int! - } - - input MoviePropsSort { - year: SortDirection - } - - input MoviePropsUpdateInput { - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int - } - - input MoviePropsWhere { - AND: [MoviePropsWhere!] - NOT: MoviePropsWhere - OR: [MoviePropsWhere!] - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int!] - year_LT: Int - year_LTE: Int - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: UGenreWhere - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createRatings(input: [RatingCreateInput!]!): CreateRatingsMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deleteRatings(delete: RatingDeleteInput, where: RatingWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateRatings(update: RatingUpdateInput, where: RatingWhere): UpdateRatingsMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - ratings(limit: Int, offset: Int, options: RatingOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [RatingSort!], where: RatingWhere): [Rating!]! - ratingsAggregate(where: RatingWhere): RatingAggregateSelection! - ratingsConnection(after: String, first: Int, sort: [RatingSort!], where: RatingWhere): RatingsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - uGenres(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: UGenreWhere): [UGenre!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - type Rating { - number: Int! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): RatingIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [RatingProductConnectionSort!], where: RatingProductConnectionWhere): RatingProductConnection! - } - - type RatingAggregateSelection { - count: Int! - number: IntAggregateSelection! - } - - input RatingConnectInput { - product: [RatingProductConnectFieldInput!] - } - - input RatingConnectOrCreateWhere { - node: RatingUniqueWhere! - } - - input RatingConnectWhere { - node: RatingWhere! - } - - input RatingCreateInput { - number: Int! - product: RatingProductFieldInput - } - - type RatingCreatedEvent { - createdRating: RatingEventPayload! - event: EventType! - timestamp: Float! - } - - input RatingDeleteInput { - product: [RatingProductDeleteFieldInput!] - } - - type RatingDeletedEvent { - deletedRating: RatingEventPayload! - event: EventType! - timestamp: Float! - } - - input RatingDisconnectInput { - product: [RatingProductDisconnectFieldInput!] - } - - type RatingEdge { - cursor: String! - node: Rating! - } - - type RatingEventPayload { - number: Int! - } - - type RatingIProductProductAggregationSelection { - count: Int! - node: RatingIProductProductNodeAggregateSelection - } - - type RatingIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input RatingOnCreateInput { - number: Int! - } - - input RatingOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more RatingSort objects to sort Ratings by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [RatingSort!] - } - - input RatingProductAggregateInput { - AND: [RatingProductAggregateInput!] - NOT: RatingProductAggregateInput - OR: [RatingProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: RatingProductNodeAggregationWhereInput - } - - input RatingProductConnectFieldInput { - connect: IProductConnectInput - where: IProductConnectWhere - } - - type RatingProductConnection { - edges: [RatingProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input RatingProductConnectionSort { - node: IProductSort - } - - input RatingProductConnectionWhere { - AND: [RatingProductConnectionWhere!] - NOT: RatingProductConnectionWhere - OR: [RatingProductConnectionWhere!] - node: IProductWhere - } - - input RatingProductCreateFieldInput { - node: IProductCreateInput! - } - - input RatingProductDeleteFieldInput { - delete: IProductDeleteInput - where: RatingProductConnectionWhere - } - - input RatingProductDisconnectFieldInput { - disconnect: IProductDisconnectInput - where: RatingProductConnectionWhere - } - - input RatingProductFieldInput { - connect: [RatingProductConnectFieldInput!] - create: [RatingProductCreateFieldInput!] - } - - input RatingProductNodeAggregationWhereInput { - AND: [RatingProductNodeAggregationWhereInput!] - NOT: RatingProductNodeAggregationWhereInput - OR: [RatingProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type RatingProductRelationship { - cursor: String! - node: IProduct! - } - - input RatingProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input RatingProductUpdateFieldInput { - connect: [RatingProductConnectFieldInput!] - create: [RatingProductCreateFieldInput!] - delete: [RatingProductDeleteFieldInput!] - disconnect: [RatingProductDisconnectFieldInput!] - update: RatingProductUpdateConnectionInput - where: RatingProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Ratings by. The order in which sorts are applied is not guaranteed when specifying many fields in one RatingSort object. - \\"\\"\\" - input RatingSort { - number: SortDirection - } - - input RatingSubscriptionWhere { - AND: [RatingSubscriptionWhere!] - NOT: RatingSubscriptionWhere - OR: [RatingSubscriptionWhere!] - number: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - number_EQ: Int - number_GT: Int - number_GTE: Int - number_IN: [Int!] - number_LT: Int - number_LTE: Int - } - - input RatingUniqueWhere { - number: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - number_EQ: Int - } - - input RatingUpdateInput { - number: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - number_DECREMENT: Int - number_INCREMENT: Int - number_SET: Int - product: [RatingProductUpdateFieldInput!] - } - - type RatingUpdatedEvent { - event: EventType! - previousState: RatingEventPayload! - timestamp: Float! - updatedRating: RatingEventPayload! - } - - input RatingWhere { - AND: [RatingWhere!] - NOT: RatingWhere - OR: [RatingWhere!] - number: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - number_EQ: Int - number_GT: Int - number_GTE: Int - number_IN: [Int!] - number_LT: Int - number_LTE: Int - productAggregate: RatingProductAggregateInput - \\"\\"\\" - Return Ratings where all of the related RatingProductConnections match this filter - \\"\\"\\" - productConnection_ALL: RatingProductConnectionWhere - \\"\\"\\" - Return Ratings where none of the related RatingProductConnections match this filter - \\"\\"\\" - productConnection_NONE: RatingProductConnectionWhere - \\"\\"\\" - Return Ratings where one of the related RatingProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: RatingProductConnectionWhere - \\"\\"\\" - Return Ratings where some of the related RatingProductConnections match this filter - \\"\\"\\" - productConnection_SOME: RatingProductConnectionWhere - \\"\\"\\"Return Ratings where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Ratings where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Ratings where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Ratings where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type RatingsConnection { - edges: [RatingEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Series implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: UGenreWhere): UGenre! - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - name: String! - } - - type SeriesAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - genre: SeriesGenreCreateInput - id: String! - name: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDeleteInput { - genre: SeriesGenreDeleteInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - id: String! - name: String! - } - - input SeriesGenreCreateInput { - Genre: SeriesGenreGenreFieldInput - Rating: SeriesGenreRatingFieldInput - } - - input SeriesGenreDeleteInput { - Genre: IProductGenreGenreDeleteFieldInput - Rating: IProductGenreRatingDeleteFieldInput - } - - input SeriesGenreGenreConnectFieldInput { - connect: GenreConnectInput - edge: SeriesPropsCreateInput - where: GenreConnectWhere - } - - input SeriesGenreGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreGenreConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: GenreOnCreateInput! - } - - input SeriesGenreGenreCreateFieldInput { - edge: SeriesPropsCreateInput - node: GenreCreateInput! - } - - input SeriesGenreGenreFieldInput { - connect: SeriesGenreGenreConnectFieldInput - connectOrCreate: SeriesGenreGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreGenreCreateFieldInput - } - - input SeriesGenreGenreUpdateConnectionInput { - edge: SeriesPropsUpdateInput - node: GenreUpdateInput - } - - input SeriesGenreGenreUpdateFieldInput { - connect: SeriesGenreGenreConnectFieldInput - connectOrCreate: SeriesGenreGenreConnectOrCreateFieldInput - create: SeriesGenreGenreCreateFieldInput - delete: IProductGenreGenreDeleteFieldInput - disconnect: IProductGenreGenreDisconnectFieldInput - update: SeriesGenreGenreUpdateConnectionInput - where: IProductGenreGenreConnectionWhere - } - - input SeriesGenreRatingConnectFieldInput { - connect: RatingConnectInput - edge: SeriesPropsCreateInput - where: RatingConnectWhere - } - - input SeriesGenreRatingConnectOrCreateFieldInput { - onCreate: SeriesGenreRatingConnectOrCreateFieldInputOnCreate! - where: RatingConnectOrCreateWhere! - } - - input SeriesGenreRatingConnectOrCreateFieldInputOnCreate { - edge: SeriesPropsCreateInput - node: RatingOnCreateInput! - } - - input SeriesGenreRatingCreateFieldInput { - edge: SeriesPropsCreateInput - node: RatingCreateInput! - } - - input SeriesGenreRatingFieldInput { - connect: SeriesGenreRatingConnectFieldInput - connectOrCreate: SeriesGenreRatingConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreRatingCreateFieldInput - } - - input SeriesGenreRatingUpdateConnectionInput { - edge: SeriesPropsUpdateInput - node: RatingUpdateInput - } - - input SeriesGenreRatingUpdateFieldInput { - connect: SeriesGenreRatingConnectFieldInput - connectOrCreate: SeriesGenreRatingConnectOrCreateFieldInput - create: SeriesGenreRatingCreateFieldInput - delete: IProductGenreRatingDeleteFieldInput - disconnect: IProductGenreRatingDisconnectFieldInput - update: SeriesGenreRatingUpdateConnectionInput - where: IProductGenreRatingConnectionWhere - } - - input SeriesGenreUpdateInput { - Genre: SeriesGenreGenreUpdateFieldInput - Rating: SeriesGenreRatingUpdateFieldInput - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - The edge properties for the following fields: - * Series.genre - \\"\\"\\" - type SeriesProps { - episodes: Int - } - - input SeriesPropsCreateInput { - episodes: Int - } - - input SeriesPropsSort { - episodes: SortDirection - } - - input SeriesPropsUpdateInput { - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - } - - input SeriesPropsWhere { - AND: [SeriesPropsWhere!] - NOT: SeriesPropsWhere - OR: [SeriesPropsWhere!] - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int] - episodes_LT: Int - episodes_LTE: Int - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - name: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input SeriesUpdateInput { - genre: SeriesGenreUpdateInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - genre: UGenreWhere - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - ratingCreated(where: RatingSubscriptionWhere): RatingCreatedEvent! - ratingDeleted(where: RatingSubscriptionWhere): RatingDeletedEvent! - ratingUpdated(where: RatingSubscriptionWhere): RatingUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - union UGenre = Genre | Rating - - input UGenreWhere { - Genre: GenreWhere - Rating: RatingWhere - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateRatingsMutationResponse { - info: UpdateInfo! - ratings: [Rating!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/issues/3537.test.ts b/packages/graphql/tests/schema/issues/3537.test.ts index c31a6e4db5..01a3769294 100644 --- a/packages/graphql/tests/schema/issues/3537.test.ts +++ b/packages/graphql/tests/schema/issues/3537.test.ts @@ -84,15 +84,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -102,28 +93,28 @@ describe("Extending the schema in when using getSubgraphSchema", () => { } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -171,15 +162,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -188,20 +170,20 @@ describe("Extending the schema in when using getSubgraphSchema", () => { } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -229,9 +211,9 @@ describe("Extending the schema in when using getSubgraphSchema", () => { type Query { _service: _Service! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -243,6 +225,20 @@ describe("Extending the schema in when using getSubgraphSchema", () => { DESC } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! @@ -346,15 +342,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -367,18 +354,18 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -401,15 +388,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -421,12 +399,12 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -445,10 +423,10 @@ describe("Extending the schema in when using getSubgraphSchema", () => { type Query { _service: _Service! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -466,6 +444,15 @@ describe("Extending the schema in when using getSubgraphSchema", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + scalar _Any type _Service { @@ -561,15 +548,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { username: String! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -582,25 +560,25 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } input ActorUpdateInput { - password: String @deprecated(reason: \\"Please use the explicit _SET field\\") - password_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String + password: StringScalarMutations + password_SET: String @deprecated(reason: \\"Please use the generic mutation 'password: { set: ... } }' instead.\\") + username: StringScalarMutations + username_SET: String @deprecated(reason: \\"Please use the generic mutation 'username: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -614,18 +592,18 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - password: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - password_CONTAINS: String - password_ENDS_WITH: String - password_EQ: String - password_IN: [String!] - password_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String + password: StringScalarFilters + password_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter password: { contains: ... }\\") + password_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { endsWith: ... }\\") + password_EQ: String @deprecated(reason: \\"Please use the relevant generic filter password: { eq: ... }\\") + password_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter password: { in: ... }\\") + password_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter password: { startsWith: ... }\\") + username: StringScalarFilters + username_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter username: { contains: ... }\\") + username_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { endsWith: ... }\\") + username_EQ: String @deprecated(reason: \\"Please use the relevant generic filter username: { eq: ... }\\") + username_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter username: { in: ... }\\") + username_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter username: { startsWith: ... }\\") } type ActorsConnection { @@ -690,15 +668,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { title: String } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -710,17 +679,17 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -734,12 +703,12 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -767,10 +736,10 @@ describe("Extending the schema in when using getSubgraphSchema", () => { type Query { _service: _Service! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -788,6 +757,20 @@ describe("Extending the schema in when using getSubgraphSchema", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorUpdated(where: ActorSubscriptionWhere): ActorUpdatedEvent! movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! diff --git a/packages/graphql/tests/schema/issues/3541.test.ts b/packages/graphql/tests/schema/issues/3541.test.ts index 1e50dc1d37..87aeedaf7c 100644 --- a/packages/graphql/tests/schema/issues/3541.test.ts +++ b/packages/graphql/tests/schema/issues/3541.test.ts @@ -79,13 +79,15 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -99,12 +101,12 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -113,10 +115,30 @@ describe("Extending the schema in when using getSubgraphSchema", () => { totalCount: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie @key(fields: \\"title\\") @shareable { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -133,7 +155,7 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -148,6 +170,25 @@ describe("Extending the schema in when using getSubgraphSchema", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -163,21 +204,22 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -195,15 +237,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -215,37 +248,39 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection @shareable { @@ -265,10 +300,10 @@ describe("Extending the schema in when using getSubgraphSchema", () => { type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! @shareable + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! @shareable moviesAggregate(where: MovieWhere): MovieAggregateSelection! @shareable moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! @shareable } @@ -286,6 +321,22 @@ describe("Extending the schema in when using getSubgraphSchema", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + scalar _Any union _Entity = Movie @@ -377,13 +428,15 @@ describe("Extending the schema in when using getSubgraphSchema", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -394,20 +447,20 @@ describe("Extending the schema in when using getSubgraphSchema", () => { } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -442,10 +495,44 @@ describe("Extending the schema in when using getSubgraphSchema", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie @key(fields: \\"title\\") @key(fields: \\"id\\") @shareable { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! id: ID! title: String! } @@ -463,7 +550,7 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -473,10 +560,6 @@ describe("Extending the schema in when using getSubgraphSchema", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -486,6 +569,25 @@ describe("Extending the schema in when using getSubgraphSchema", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -518,21 +620,22 @@ describe("Extending the schema in when using getSubgraphSchema", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -565,53 +668,55 @@ describe("Extending the schema in when using getSubgraphSchema", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type Mutation { @@ -634,7 +739,7 @@ describe("Extending the schema in when using getSubgraphSchema", () => { type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! } @@ -652,6 +757,27 @@ describe("Extending the schema in when using getSubgraphSchema", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/3698.test.ts b/packages/graphql/tests/schema/issues/3698.test.ts index 32a30bf166..5263efe9eb 100644 --- a/packages/graphql/tests/schema/issues/3698.test.ts +++ b/packages/graphql/tests/schema/issues/3698.test.ts @@ -31,7 +31,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { id: String! name: String! - genre: Genre! + genre: [Genre!]! info: String! } @@ -39,12 +39,12 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { id: String! name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) + genre: [Genre!]! @relationship(type: "HAS_GENRE", direction: OUT) info: String! @customResolver(requires: "id name") } type Genre @node { - name: String! @unique + name: String! product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) } `; @@ -105,11 +105,21 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { UPDATE } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type Genre { name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! + product(limit: Int, offset: Int, sort: [IProductSort!], where: IProductWhere): [IProduct!]! + productAggregate(where: IProductWhere): GenreIProductProductAggregationSelection + productConnection(after: String, first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! } type GenreAggregateSelection { @@ -121,10 +131,6 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { product: [GenreProductConnectFieldInput!] } - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - input GenreConnectWhere { node: GenreWhere! } @@ -174,24 +180,11 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { name: StringAggregateSelection! } - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - input GenreProductAggregateInput { AND: [GenreProductAggregateInput!] NOT: GenreProductAggregateInput OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -210,6 +203,25 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { totalCount: Int! } + input GenreProductConnectionFilters { + \\"\\"\\" + Return Genres where all of the related GenreProductConnections match this filter + \\"\\"\\" + all: GenreProductConnectionWhere + \\"\\"\\" + Return Genres where none of the related GenreProductConnections match this filter + \\"\\"\\" + none: GenreProductConnectionWhere + \\"\\"\\" + Return Genres where one of the related GenreProductConnections match this filter + \\"\\"\\" + single: GenreProductConnectionWhere + \\"\\"\\" + Return Genres where some of the related GenreProductConnections match this filter + \\"\\"\\" + some: GenreProductConnectionWhere + } + input GenreProductConnectionSort { node: IProductSort } @@ -242,51 +254,54 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [GenreProductNodeAggregationWhereInput!] NOT: GenreProductNodeAggregationWhereInput OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - info_AVERAGE_LENGTH_EQUAL: Float - info_AVERAGE_LENGTH_GT: Float - info_AVERAGE_LENGTH_GTE: Float - info_AVERAGE_LENGTH_LT: Float - info_AVERAGE_LENGTH_LTE: Float - info_LONGEST_LENGTH_EQUAL: Int - info_LONGEST_LENGTH_GT: Int - info_LONGEST_LENGTH_GTE: Int - info_LONGEST_LENGTH_LT: Int - info_LONGEST_LENGTH_LTE: Int - info_SHORTEST_LENGTH_EQUAL: Int - info_SHORTEST_LENGTH_GT: Int - info_SHORTEST_LENGTH_GTE: Int - info_SHORTEST_LENGTH_LT: Int - info_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + id: StringScalarAggregationFilters + id_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { eq: ... } } }' instead.\\") + id_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { gt: ... } } }' instead.\\") + id_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { gte: ... } } }' instead.\\") + id_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { lt: ... } } }' instead.\\") + id_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { lte: ... } } }' instead.\\") + id_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { eq: ... } } }' instead.\\") + id_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { gt: ... } } }' instead.\\") + id_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { gte: ... } } }' instead.\\") + id_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { lt: ... } } }' instead.\\") + id_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { lte: ... } } }' instead.\\") + id_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { eq: ... } } }' instead.\\") + id_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { gt: ... } } }' instead.\\") + id_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { gte: ... } } }' instead.\\") + id_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { lt: ... } } }' instead.\\") + id_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { lte: ... } } }' instead.\\") + info: StringScalarAggregationFilters + info_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'info: { averageLength: { eq: ... } } }' instead.\\") + info_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'info: { averageLength: { gt: ... } } }' instead.\\") + info_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'info: { averageLength: { gte: ... } } }' instead.\\") + info_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'info: { averageLength: { lt: ... } } }' instead.\\") + info_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'info: { averageLength: { lte: ... } } }' instead.\\") + info_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { longestLength: { eq: ... } } }' instead.\\") + info_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { longestLength: { gt: ... } } }' instead.\\") + info_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { longestLength: { gte: ... } } }' instead.\\") + info_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { longestLength: { lt: ... } } }' instead.\\") + info_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { longestLength: { lte: ... } } }' instead.\\") + info_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { shortestLength: { eq: ... } } }' instead.\\") + info_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { shortestLength: { gt: ... } } }' instead.\\") + info_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { shortestLength: { gte: ... } } }' instead.\\") + info_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { shortestLength: { lt: ... } } }' instead.\\") + info_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'info: { shortestLength: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type GenreProductRelationship { @@ -307,6 +322,17 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { where: GenreProductConnectionWhere } + input GenreRelationshipFilters { + \\"\\"\\"Filter type where all of the related Genres match this filter\\"\\"\\" + all: GenreWhere + \\"\\"\\"Filter type where none of the related Genres match this filter\\"\\"\\" + none: GenreWhere + \\"\\"\\"Filter type where one of the related Genres match this filter\\"\\"\\" + single: GenreWhere + \\"\\"\\"Filter type where some of the related Genres match this filter\\"\\"\\" + some: GenreWhere + } + \\"\\"\\" Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. \\"\\"\\" @@ -318,22 +344,17 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [GenreSubscriptionWhere!] NOT: GenreSubscriptionWhere OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") product: [GenreProductUpdateFieldInput!] } @@ -348,37 +369,39 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + product: IProductRelationshipFilters productAggregate: GenreProductAggregateInput + productConnection: GenreProductConnectionFilters \\"\\"\\" Return Genres where all of the related GenreProductConnections match this filter \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere + productConnection_ALL: GenreProductConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'productConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where none of the related GenreProductConnections match this filter \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere + productConnection_NONE: GenreProductConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'productConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where one of the related GenreProductConnections match this filter \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere + productConnection_SINGLE: GenreProductConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'productConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Genres where some of the related GenreProductConnections match this filter \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere + productConnection_SOME: GenreProductConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'productConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere + product_ALL: IProductWhere @deprecated(reason: \\"Please use the relevant generic filter 'product: { all: ... }' instead.\\") \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere + product_NONE: IProductWhere @deprecated(reason: \\"Please use the relevant generic filter 'product: { none: ... }' instead.\\") \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere + product_SINGLE: IProductWhere @deprecated(reason: \\"Please use the relevant generic filter 'product: { single: ... }' instead.\\") \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere + product_SOME: IProductWhere @deprecated(reason: \\"Please use the relevant generic filter 'product: { some: ... }' instead.\\") } type GenresConnection { @@ -388,7 +411,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } interface IProduct { - genre: Genre! + genre: [Genre!]! id: String! info: String! name: String! @@ -418,13 +441,15 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { Movie } - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] + input IProductRelationshipFilters { + \\"\\"\\"Filter type where all of the related IProducts match this filter\\"\\"\\" + all: IProductWhere + \\"\\"\\"Filter type where none of the related IProducts match this filter\\"\\"\\" + none: IProductWhere + \\"\\"\\"Filter type where one of the related IProducts match this filter\\"\\"\\" + single: IProductWhere + \\"\\"\\"Filter type where some of the related IProducts match this filter\\"\\"\\" + some: IProductWhere } \\"\\"\\" @@ -437,38 +462,37 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } input IProductUpdateInput { - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - info: String @deprecated(reason: \\"Please use the explicit _SET field\\") - info_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + id: StringScalarMutations + id_SET: String @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + info: StringScalarMutations + info_SET: String @deprecated(reason: \\"Please use the generic mutation 'info: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input IProductWhere { AND: [IProductWhere!] NOT: IProductWhere OR: [IProductWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - info: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - info_CONTAINS: String - info_ENDS_WITH: String - info_EQ: String - info_IN: [String!] - info_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + id: StringScalarFilters + id_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: String @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + info: StringScalarFilters + info_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter info: { contains: ... }\\") + info_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter info: { endsWith: ... }\\") + info_EQ: String @deprecated(reason: \\"Please use the relevant generic filter info: { eq: ... }\\") + info_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter info: { in: ... }\\") + info_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter info: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type IProductsConnection { @@ -477,10 +501,20 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { totalCount: Int! } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! + genre(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! + genreAggregate(where: GenreWhere): MovieGenreGenreAggregationSelection + genreConnection(after: String, first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! id: String! info: String! name: String! @@ -505,7 +539,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } input MovieDeleteInput { - genre: MovieGenreDeleteFieldInput + genre: [MovieGenreDeleteFieldInput!] } type MovieDeletedEvent { @@ -528,7 +562,7 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [MovieGenreAggregateInput!] NOT: MovieGenreAggregateInput OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -538,29 +572,35 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [GenreConnectInput!] where: GenreConnectWhere } - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - type MovieGenreConnection { edges: [MovieGenreRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieGenreConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieGenreConnections match this filter + \\"\\"\\" + all: MovieGenreConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieGenreConnections match this filter + \\"\\"\\" + none: MovieGenreConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieGenreConnections match this filter + \\"\\"\\" + single: MovieGenreConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieGenreConnections match this filter + \\"\\"\\" + some: MovieGenreConnectionWhere + } + input MovieGenreConnectionSort { node: GenreSort } @@ -587,9 +627,8 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput + connect: [MovieGenreConnectFieldInput!] + create: [MovieGenreCreateFieldInput!] } type MovieGenreGenreAggregationSelection { @@ -605,21 +644,22 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [MovieGenreNodeAggregationWhereInput!] NOT: MovieGenreNodeAggregationWhereInput OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieGenreRelationship { @@ -632,24 +672,14 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: MovieGenreDeleteFieldInput - disconnect: MovieGenreDisconnectFieldInput + connect: [MovieGenreConnectFieldInput!] + create: [MovieGenreCreateFieldInput!] + delete: [MovieGenreDeleteFieldInput!] + disconnect: [MovieGenreDisconnectFieldInput!] update: MovieGenreUpdateConnectionInput where: MovieGenreConnectionWhere } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -662,26 +692,26 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + id: StringScalarFilters + id_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: String @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + genre: [MovieGenreUpdateFieldInput!] + id: StringScalarMutations + id_SET: String @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -695,21 +725,45 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - genre: GenreWhere + genre: GenreRelationshipFilters genreAggregate: MovieGenreAggregateInput - genreConnection: MovieGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + genreConnection: MovieGenreConnectionFilters + \\"\\"\\" + Return Movies where all of the related MovieGenreConnections match this filter + \\"\\"\\" + genreConnection_ALL: MovieGenreConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genreConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related MovieGenreConnections match this filter + \\"\\"\\" + genreConnection_NONE: MovieGenreConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genreConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related MovieGenreConnections match this filter + \\"\\"\\" + genreConnection_SINGLE: MovieGenreConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genreConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related MovieGenreConnections match this filter + \\"\\"\\" + genreConnection_SOME: MovieGenreConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'genreConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Genres match this filter\\"\\"\\" + genre_ALL: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genre: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Genres match this filter\\"\\"\\" + genre_NONE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genre: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Genres match this filter\\"\\"\\" + genre_SINGLE: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genre: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Genres match this filter\\"\\"\\" + genre_SOME: GenreWhere @deprecated(reason: \\"Please use the relevant generic filter 'genre: { some: ... }' instead.\\") + id: StringScalarFilters + id_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: String @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type MoviesConnection { @@ -736,13 +790,13 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! + iProducts(limit: Int, offset: Int, sort: [IProductSort!], where: IProductWhere): [IProduct!]! iProductsAggregate(where: IProductWhere): IProductAggregateSelection! iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -760,6 +814,27 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! @@ -790,1972 +865,4 @@ describe("https://github.com/neo4j/graphql/issues/3698", () => { }" `); }); - - test("Relationship declared in interface", async () => { - const typeDefs = gql` - interface IProduct { - id: String! - - name: String! - genre: Genre! @declareRelationship - info: String! - } - - type Movie implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - info: String! @customResolver(requires: "id name") - } - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const resolvers = { - Movie: { - info: ({ id, name }) => { - return `${id}, ${name}`; - }, - }, - }; - const neoSchema = new Neo4jGraphQL({ typeDefs, resolvers, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - info: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - connect: IProductConnectInput - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - delete: IProductDeleteInput - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - disconnect: IProductDisconnectInput - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - info_AVERAGE_LENGTH_EQUAL: Float - info_AVERAGE_LENGTH_GT: Float - info_AVERAGE_LENGTH_GTE: Float - info_AVERAGE_LENGTH_LT: Float - info_AVERAGE_LENGTH_LTE: Float - info_LONGEST_LENGTH_EQUAL: Int - info_LONGEST_LENGTH_GT: Int - info_LONGEST_LENGTH_GTE: Int - info_LONGEST_LENGTH_LT: Int - info_LONGEST_LENGTH_LTE: Int - info_SHORTEST_LENGTH_EQUAL: Int - info_SHORTEST_LENGTH_GT: Int - info_SHORTEST_LENGTH_GTE: Int - info_SHORTEST_LENGTH_LT: Int - info_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreConnection(after: String, first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - info: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - info: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectInput { - genre: IProductGenreConnectFieldInput - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - } - - input IProductDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - input IProductDisconnectInput { - genre: IProductGenreDisconnectFieldInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - input IProductGenreAggregateInput { - AND: [IProductGenreAggregateInput!] - NOT: IProductGenreAggregateInput - OR: [IProductGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: IProductGenreNodeAggregationWhereInput - } - - input IProductGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type IProductGenreConnection { - edges: [IProductGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input IProductGenreConnectionSort { - node: GenreSort - } - - input IProductGenreConnectionWhere { - AND: [IProductGenreConnectionWhere!] - NOT: IProductGenreConnectionWhere - OR: [IProductGenreConnectionWhere!] - node: GenreWhere - } - - input IProductGenreCreateFieldInput { - node: GenreCreateInput! - } - - input IProductGenreDeleteFieldInput { - delete: GenreDeleteInput - where: IProductGenreConnectionWhere - } - - input IProductGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: IProductGenreConnectionWhere - } - - input IProductGenreNodeAggregationWhereInput { - AND: [IProductGenreNodeAggregationWhereInput!] - NOT: IProductGenreNodeAggregationWhereInput - OR: [IProductGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type IProductGenreRelationship { - cursor: String! - node: Genre! - } - - input IProductGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input IProductGenreUpdateFieldInput { - connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput - create: IProductGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: IProductGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - enum IProductImplementation { - Movie - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - info: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - genre: IProductGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - info: String @deprecated(reason: \\"Please use the explicit _SET field\\") - info_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - genre: GenreWhere - genreAggregate: IProductGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - info: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - info_CONTAINS: String - info_ENDS_WITH: String - info_EQ: String - info_IN: [String!] - info_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - info: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - input MovieGenreCreateFieldInput { - node: GenreCreateInput! - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - input MovieGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: MovieGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("Relationship declared in interface, customResolver field in one implementation only", async () => { - const typeDefs = gql` - interface IProduct { - id: String! - - name: String! - genre: Genre! @declareRelationship - info: String! - } - - type Movie implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - info: String! @customResolver(requires: "id name") - } - - type Series implements IProduct @node { - id: String! - - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - info: String! - } - - type Genre @node { - name: String! @unique - product: [IProduct!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const resolvers = { - Movie: { - info: ({ id, name }) => { - return `${id}, ${name}`; - }, - }, - }; - const neoSchema = new Neo4jGraphQL({ typeDefs, resolvers, features: { subscriptions: new TestCDCEngine() } }); - - const schema = await neoSchema.getSchema(); - const errors = validateSchema(schema); - expect(errors).toHaveLength(0); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type Genre { - name: String! - product(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - productAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: IProductWhere): GenreIProductProductAggregationSelection - productConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreProductConnectionSort!], where: GenreProductConnectionWhere): GenreProductConnection! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - product: [GenreProductConnectFieldInput!] - } - - input GenreConnectOrCreateWhere { - node: GenreUniqueWhere! - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - name: String! - product: GenreProductFieldInput - } - - type GenreCreatedEvent { - createdGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDeleteInput { - product: [GenreProductDeleteFieldInput!] - } - - type GenreDeletedEvent { - deletedGenre: GenreEventPayload! - event: EventType! - timestamp: Float! - } - - input GenreDisconnectInput { - product: [GenreProductDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreEventPayload { - name: String! - } - - type GenreIProductProductAggregationSelection { - count: Int! - node: GenreIProductProductNodeAggregateSelection - } - - type GenreIProductProductNodeAggregateSelection { - id: StringAggregateSelection! - info: StringAggregateSelection! - name: StringAggregateSelection! - } - - input GenreOnCreateInput { - name: String! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - input GenreProductAggregateInput { - AND: [GenreProductAggregateInput!] - NOT: GenreProductAggregateInput - OR: [GenreProductAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreProductNodeAggregationWhereInput - } - - input GenreProductConnectFieldInput { - connect: IProductConnectInput - where: IProductConnectWhere - } - - type GenreProductConnection { - edges: [GenreProductRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreProductConnectionSort { - node: IProductSort - } - - input GenreProductConnectionWhere { - AND: [GenreProductConnectionWhere!] - NOT: GenreProductConnectionWhere - OR: [GenreProductConnectionWhere!] - node: IProductWhere - } - - input GenreProductCreateFieldInput { - node: IProductCreateInput! - } - - input GenreProductDeleteFieldInput { - delete: IProductDeleteInput - where: GenreProductConnectionWhere - } - - input GenreProductDisconnectFieldInput { - disconnect: IProductDisconnectInput - where: GenreProductConnectionWhere - } - - input GenreProductFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - } - - input GenreProductNodeAggregationWhereInput { - AND: [GenreProductNodeAggregationWhereInput!] - NOT: GenreProductNodeAggregationWhereInput - OR: [GenreProductNodeAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int - info_AVERAGE_LENGTH_EQUAL: Float - info_AVERAGE_LENGTH_GT: Float - info_AVERAGE_LENGTH_GTE: Float - info_AVERAGE_LENGTH_LT: Float - info_AVERAGE_LENGTH_LTE: Float - info_LONGEST_LENGTH_EQUAL: Int - info_LONGEST_LENGTH_GT: Int - info_LONGEST_LENGTH_GTE: Int - info_LONGEST_LENGTH_LT: Int - info_LONGEST_LENGTH_LTE: Int - info_SHORTEST_LENGTH_EQUAL: Int - info_SHORTEST_LENGTH_GT: Int - info_SHORTEST_LENGTH_GTE: Int - info_SHORTEST_LENGTH_LT: Int - info_SHORTEST_LENGTH_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreProductRelationship { - cursor: String! - node: IProduct! - } - - input GenreProductUpdateConnectionInput { - node: IProductUpdateInput - } - - input GenreProductUpdateFieldInput { - connect: [GenreProductConnectFieldInput!] - create: [GenreProductCreateFieldInput!] - delete: [GenreProductDeleteFieldInput!] - disconnect: [GenreProductDisconnectFieldInput!] - update: GenreProductUpdateConnectionInput - where: GenreProductConnectionWhere - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreSubscriptionWhere { - AND: [GenreSubscriptionWhere!] - NOT: GenreSubscriptionWhere - OR: [GenreSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input GenreUniqueWhere { - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_EQ: String - } - - input GenreUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - product: [GenreProductUpdateFieldInput!] - } - - type GenreUpdatedEvent { - event: EventType! - previousState: GenreEventPayload! - timestamp: Float! - updatedGenre: GenreEventPayload! - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - productAggregate: GenreProductAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_ALL: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_NONE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SINGLE: GenreProductConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreProductConnections match this filter - \\"\\"\\" - productConnection_SOME: GenreProductConnectionWhere - \\"\\"\\"Return Genres where all of the related IProducts match this filter\\"\\"\\" - product_ALL: IProductWhere - \\"\\"\\"Return Genres where none of the related IProducts match this filter\\"\\"\\" - product_NONE: IProductWhere - \\"\\"\\"Return Genres where one of the related IProducts match this filter\\"\\"\\" - product_SINGLE: IProductWhere - \\"\\"\\"Return Genres where some of the related IProducts match this filter\\"\\"\\" - product_SOME: IProductWhere - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - interface IProduct { - genre(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreConnection(after: String, first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - info: String! - name: String! - } - - type IProductAggregateSelection { - count: Int! - id: StringAggregateSelection! - info: StringAggregateSelection! - name: StringAggregateSelection! - } - - input IProductConnectInput { - genre: IProductGenreConnectFieldInput - } - - input IProductConnectWhere { - node: IProductWhere! - } - - input IProductCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - input IProductDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - input IProductDisconnectInput { - genre: IProductGenreDisconnectFieldInput - } - - type IProductEdge { - cursor: String! - node: IProduct! - } - - input IProductGenreAggregateInput { - AND: [IProductGenreAggregateInput!] - NOT: IProductGenreAggregateInput - OR: [IProductGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: IProductGenreNodeAggregationWhereInput - } - - input IProductGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input IProductGenreConnectOrCreateFieldInput { - onCreate: IProductGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input IProductGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - type IProductGenreConnection { - edges: [IProductGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input IProductGenreConnectionSort { - node: GenreSort - } - - input IProductGenreConnectionWhere { - AND: [IProductGenreConnectionWhere!] - NOT: IProductGenreConnectionWhere - OR: [IProductGenreConnectionWhere!] - node: GenreWhere - } - - input IProductGenreCreateFieldInput { - node: GenreCreateInput! - } - - input IProductGenreDeleteFieldInput { - delete: GenreDeleteInput - where: IProductGenreConnectionWhere - } - - input IProductGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: IProductGenreConnectionWhere - } - - input IProductGenreNodeAggregationWhereInput { - AND: [IProductGenreNodeAggregationWhereInput!] - NOT: IProductGenreNodeAggregationWhereInput - OR: [IProductGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type IProductGenreRelationship { - cursor: String! - node: Genre! - } - - input IProductGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input IProductGenreUpdateFieldInput { - connect: IProductGenreConnectFieldInput - connectOrCreate: IProductGenreConnectOrCreateFieldInput - create: IProductGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: IProductGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - enum IProductImplementation { - Movie - Series - } - - input IProductOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more IProductSort objects to sort IProducts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [IProductSort!] - } - - \\"\\"\\" - Fields to sort IProducts by. The order in which sorts are applied is not guaranteed when specifying many fields in one IProductSort object. - \\"\\"\\" - input IProductSort { - id: SortDirection - info: SortDirection - name: SortDirection - } - - input IProductUpdateInput { - genre: IProductGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - info: String @deprecated(reason: \\"Please use the explicit _SET field\\") - info_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input IProductWhere { - AND: [IProductWhere!] - NOT: IProductWhere - OR: [IProductWhere!] - genre: GenreWhere - genreAggregate: IProductGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - info: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - info_CONTAINS: String - info_ENDS_WITH: String - info_EQ: String - info_IN: [String!] - info_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - typename: [IProductImplementation!] - typename_IN: [IProductImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type IProductsConnection { - edges: [IProductEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - info: String! - name: String! - } - - type MovieAggregateSelection { - count: Int! - id: StringAggregateSelection! - name: StringAggregateSelection! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - id: String! - name: String! - } - - type MovieCreatedEvent { - createdMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - input MovieDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - type MovieDeletedEvent { - deletedMovie: MovieEventPayload! - event: EventType! - timestamp: Float! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - type MovieEventPayload { - id: String! - name: String! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input MovieGenreConnectOrCreateFieldInput { - onCreate: MovieGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input MovieGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - input MovieGenreCreateFieldInput { - node: GenreCreateInput! - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: MovieGenreCreateFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - input MovieGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - connectOrCreate: MovieGenreConnectOrCreateFieldInput - create: MovieGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: MovieGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - name: SortDirection - } - - input MovieSubscriptionWhere { - AND: [MovieSubscriptionWhere!] - NOT: MovieSubscriptionWhere - OR: [MovieSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type MovieUpdatedEvent { - event: EventType! - previousState: MovieEventPayload! - timestamp: Float! - updatedMovie: MovieEventPayload! - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - iProducts(limit: Int, offset: Int, options: IProductOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [IProductSort!], where: IProductWhere): [IProduct!]! - iProductsAggregate(where: IProductWhere): IProductAggregateSelection! - iProductsConnection(after: String, first: Int, sort: [IProductSort!], where: IProductWhere): IProductsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements IProduct { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): SeriesGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [IProductGenreConnectionSort!], where: IProductGenreConnectionWhere): IProductGenreConnection! - id: String! - info: String! - name: String! - } - - type SeriesAggregateSelection { - count: Int! - id: StringAggregateSelection! - info: StringAggregateSelection! - name: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - genre: SeriesGenreFieldInput - id: String! - info: String! - name: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDeleteInput { - genre: IProductGenreDeleteFieldInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - id: String! - info: String! - name: String! - } - - input SeriesGenreAggregateInput { - AND: [SeriesGenreAggregateInput!] - NOT: SeriesGenreAggregateInput - OR: [SeriesGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: SeriesGenreNodeAggregationWhereInput - } - - input SeriesGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - input SeriesGenreConnectOrCreateFieldInput { - onCreate: SeriesGenreConnectOrCreateFieldInputOnCreate! - where: GenreConnectOrCreateWhere! - } - - input SeriesGenreConnectOrCreateFieldInputOnCreate { - node: GenreOnCreateInput! - } - - input SeriesGenreCreateFieldInput { - node: GenreCreateInput! - } - - input SeriesGenreFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") - create: SeriesGenreCreateFieldInput - } - - type SeriesGenreGenreAggregationSelection { - count: Int! - node: SeriesGenreGenreNodeAggregateSelection - } - - type SeriesGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input SeriesGenreNodeAggregationWhereInput { - AND: [SeriesGenreNodeAggregationWhereInput!] - NOT: SeriesGenreNodeAggregationWhereInput - OR: [SeriesGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - input SeriesGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input SeriesGenreUpdateFieldInput { - connect: SeriesGenreConnectFieldInput - connectOrCreate: SeriesGenreConnectOrCreateFieldInput - create: SeriesGenreCreateFieldInput - delete: IProductGenreDeleteFieldInput - disconnect: IProductGenreDisconnectFieldInput - update: SeriesGenreUpdateConnectionInput - where: IProductGenreConnectionWhere - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - info: SortDirection - name: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - info: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - info_CONTAINS: String - info_ENDS_WITH: String - info_EQ: String - info_IN: [String!] - info_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - input SeriesUpdateInput { - genre: SeriesGenreUpdateFieldInput - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String - info: String @deprecated(reason: \\"Please use the explicit _SET field\\") - info_SET: String - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - genre: GenreWhere - genreAggregate: SeriesGenreAggregateInput - genreConnection: IProductGenreConnectionWhere - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String!] - id_STARTS_WITH: String - info: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - info_CONTAINS: String - info_ENDS_WITH: String - info_EQ: String - info_IN: [String!] - info_STARTS_WITH: String - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - genreCreated(where: GenreSubscriptionWhere): GenreCreatedEvent! - genreDeleted(where: GenreSubscriptionWhere): GenreDeletedEvent! - genreUpdated(where: GenreSubscriptionWhere): GenreUpdatedEvent! - movieCreated(where: MovieSubscriptionWhere): MovieCreatedEvent! - movieDeleted(where: MovieSubscriptionWhere): MovieDeletedEvent! - movieUpdated(where: MovieSubscriptionWhere): MovieUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); }); diff --git a/packages/graphql/tests/schema/issues/3816.test.ts b/packages/graphql/tests/schema/issues/3816.test.ts deleted file mode 100644 index 2b509d0e2d..0000000000 --- a/packages/graphql/tests/schema/issues/3816.test.ts +++ /dev/null @@ -1,943 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("https://github.com/neo4j/graphql/issues/3816", () => { - test("Combination of nested operations in reported issue", async () => { - const typeDefs = gql` - type Movie @node { - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT, nestedOperations: [CONNECT, DISCONNECT]) - } - - type Genre @node { - name: String! - movies: [Movie!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Genre { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): GenreMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! - name: String! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreConnectInput { - movies: [GenreMoviesConnectFieldInput!] - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - movies: GenreMoviesFieldInput - name: String! - } - - input GenreDeleteInput { - movies: [GenreMoviesDeleteFieldInput!] - } - - input GenreDisconnectInput { - movies: [GenreMoviesDisconnectFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreMovieMoviesAggregationSelection { - count: Int! - node: GenreMovieMoviesNodeAggregateSelection - } - - type GenreMovieMoviesNodeAggregateSelection { - name: StringAggregateSelection! - } - - input GenreMoviesAggregateInput { - AND: [GenreMoviesAggregateInput!] - NOT: GenreMoviesAggregateInput - OR: [GenreMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreMoviesNodeAggregationWhereInput - } - - input GenreMoviesConnectFieldInput { - connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type GenreMoviesConnection { - edges: [GenreMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreMoviesConnectionSort { - node: MovieSort - } - - input GenreMoviesConnectionWhere { - AND: [GenreMoviesConnectionWhere!] - NOT: GenreMoviesConnectionWhere - OR: [GenreMoviesConnectionWhere!] - node: MovieWhere - } - - input GenreMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input GenreMoviesDeleteFieldInput { - where: GenreMoviesConnectionWhere - } - - input GenreMoviesDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: GenreMoviesConnectionWhere - } - - input GenreMoviesFieldInput { - connect: [GenreMoviesConnectFieldInput!] - create: [GenreMoviesCreateFieldInput!] - } - - input GenreMoviesNodeAggregationWhereInput { - AND: [GenreMoviesNodeAggregationWhereInput!] - NOT: GenreMoviesNodeAggregationWhereInput - OR: [GenreMoviesNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreMoviesRelationship { - cursor: String! - node: Movie! - } - - input GenreMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input GenreMoviesUpdateFieldInput { - connect: [GenreMoviesConnectFieldInput!] - create: [GenreMoviesCreateFieldInput!] - delete: [GenreMoviesDeleteFieldInput!] - disconnect: [GenreMoviesDisconnectFieldInput!] - update: GenreMoviesUpdateConnectionInput - where: GenreMoviesConnectionWhere - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreUpdateInput { - movies: [GenreMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - moviesAggregate: GenreMoviesAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: GenreMoviesConnectionWhere - \\"\\"\\"Return Genres where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Genres where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Genres where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Genres where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! - name: String! - } - - type MovieAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input MovieConnectInput { - genre: MovieGenreConnectFieldInput - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - genre: MovieGenreFieldInput - name: String! - } - - input MovieDisconnectInput { - genre: MovieGenreDisconnectFieldInput - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - input MovieGenreConnectFieldInput { - connect: GenreConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: GenreConnectWhere - } - - type MovieGenreConnection { - edges: [MovieGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieGenreConnectionSort { - node: GenreSort - } - - input MovieGenreConnectionWhere { - AND: [MovieGenreConnectionWhere!] - NOT: MovieGenreConnectionWhere - OR: [MovieGenreConnectionWhere!] - node: GenreWhere - } - - input MovieGenreDisconnectFieldInput { - disconnect: GenreDisconnectInput - where: MovieGenreConnectionWhere - } - - input MovieGenreFieldInput { - connect: MovieGenreConnectFieldInput - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieGenreRelationship { - cursor: String! - node: Genre! - } - - input MovieGenreUpdateFieldInput { - connect: MovieGenreConnectFieldInput - disconnect: MovieGenreDisconnectFieldInput - where: MovieGenreConnectionWhere - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - name: SortDirection - } - - input MovieUpdateInput { - genre: MovieGenreUpdateFieldInput - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: MovieGenreConnectionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("No nested operations in one type", async () => { - const typeDefs = gql` - type Movie @node { - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT, nestedOperations: []) - } - - type Genre @node { - name: String! - movies: [Movie!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Genre { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): GenreMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [GenreMoviesConnectionSort!], where: GenreMoviesConnectionWhere): GenreMoviesConnection! - name: String! - } - - type GenreAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input GenreCreateInput { - movies: GenreMoviesFieldInput - name: String! - } - - input GenreDeleteInput { - movies: [GenreMoviesDeleteFieldInput!] - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - type GenreMovieMoviesAggregationSelection { - count: Int! - node: GenreMovieMoviesNodeAggregateSelection - } - - type GenreMovieMoviesNodeAggregateSelection { - name: StringAggregateSelection! - } - - input GenreMoviesAggregateInput { - AND: [GenreMoviesAggregateInput!] - NOT: GenreMoviesAggregateInput - OR: [GenreMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: GenreMoviesNodeAggregationWhereInput - } - - input GenreMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type GenreMoviesConnection { - edges: [GenreMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input GenreMoviesConnectionSort { - node: MovieSort - } - - input GenreMoviesConnectionWhere { - AND: [GenreMoviesConnectionWhere!] - NOT: GenreMoviesConnectionWhere - OR: [GenreMoviesConnectionWhere!] - node: MovieWhere - } - - input GenreMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input GenreMoviesDeleteFieldInput { - where: GenreMoviesConnectionWhere - } - - input GenreMoviesDisconnectFieldInput { - where: GenreMoviesConnectionWhere - } - - input GenreMoviesFieldInput { - connect: [GenreMoviesConnectFieldInput!] - create: [GenreMoviesCreateFieldInput!] - } - - input GenreMoviesNodeAggregationWhereInput { - AND: [GenreMoviesNodeAggregationWhereInput!] - NOT: GenreMoviesNodeAggregationWhereInput - OR: [GenreMoviesNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type GenreMoviesRelationship { - cursor: String! - node: Movie! - } - - input GenreMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input GenreMoviesUpdateFieldInput { - connect: [GenreMoviesConnectFieldInput!] - create: [GenreMoviesCreateFieldInput!] - delete: [GenreMoviesDeleteFieldInput!] - disconnect: [GenreMoviesDisconnectFieldInput!] - update: GenreMoviesUpdateConnectionInput - where: GenreMoviesConnectionWhere - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - name: SortDirection - } - - input GenreUpdateInput { - movies: [GenreMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - moviesAggregate: GenreMoviesAggregateInput - \\"\\"\\" - Return Genres where all of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where none of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where one of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: GenreMoviesConnectionWhere - \\"\\"\\" - Return Genres where some of the related GenreMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: GenreMoviesConnectionWhere - \\"\\"\\"Return Genres where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Genres where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Genres where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Genres where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Movie { - genre(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): Genre! - genreAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: GenreWhere): MovieGenreGenreAggregationSelection - genreConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieGenreConnectionSort!], where: MovieGenreConnectionWhere): MovieGenreConnection! - name: String! - } - - type MovieAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - name: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieGenreAggregateInput { - AND: [MovieGenreAggregateInput!] - NOT: MovieGenreAggregateInput - OR: [MovieGenreAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieGenreNodeAggregationWhereInput - } - - type MovieGenreConnection { - edges: [MovieGenreRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieGenreConnectionSort { - node: GenreSort - } - - input MovieGenreConnectionWhere { - AND: [MovieGenreConnectionWhere!] - NOT: MovieGenreConnectionWhere - OR: [MovieGenreConnectionWhere!] - node: GenreWhere - } - - type MovieGenreGenreAggregationSelection { - count: Int! - node: MovieGenreGenreNodeAggregateSelection - } - - type MovieGenreGenreNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieGenreNodeAggregationWhereInput { - AND: [MovieGenreNodeAggregationWhereInput!] - NOT: MovieGenreNodeAggregationWhereInput - OR: [MovieGenreNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieGenreRelationship { - cursor: String! - node: Genre! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - name: SortDirection - } - - input MovieUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - genre: GenreWhere - genreAggregate: MovieGenreAggregateInput - genreConnection: MovieGenreConnectionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteGenres(delete: GenreDeleteInput, where: GenreWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/issues/3817.test.ts b/packages/graphql/tests/schema/issues/3817.test.ts index 25b25c2373..e32e4d63c1 100644 --- a/packages/graphql/tests/schema/issues/3817.test.ts +++ b/packages/graphql/tests/schema/issues/3817.test.ts @@ -22,18 +22,13 @@ import { gql } from "graphql-tag"; import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; -describe("3817", () => { +describe("https://github.com/neo4j/graphql/issues/3817", () => { test("3817", async () => { const typeDefs = gql` type Person @node { - id: ID! @id @unique + id: ID! @id friends: [Person!]! - @relationship( - type: "FRIEND_OF" - direction: OUT - queryDirection: UNDIRECTED_ONLY - properties: "FriendOf" - ) + @relationship(type: "FRIEND_OF", direction: OUT, queryDirection: UNDIRECTED, properties: "FriendOf") } type FriendOf @relationshipProperties { @@ -87,6 +82,16 @@ describe("3817", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + \\"\\"\\" The edge properties for the following fields: * Person.friends @@ -99,21 +104,22 @@ describe("3817", () => { AND: [FriendOfAggregationWhereInput!] NOT: FriendOfAggregationWhereInput OR: [FriendOfAggregationWhereInput!] - id_AVERAGE_LENGTH_EQUAL: Float - id_AVERAGE_LENGTH_GT: Float - id_AVERAGE_LENGTH_GTE: Float - id_AVERAGE_LENGTH_LT: Float - id_AVERAGE_LENGTH_LTE: Float - id_LONGEST_LENGTH_EQUAL: Int - id_LONGEST_LENGTH_GT: Int - id_LONGEST_LENGTH_GTE: Int - id_LONGEST_LENGTH_LT: Int - id_LONGEST_LENGTH_LTE: Int - id_SHORTEST_LENGTH_EQUAL: Int - id_SHORTEST_LENGTH_GT: Int - id_SHORTEST_LENGTH_GTE: Int - id_SHORTEST_LENGTH_LT: Int - id_SHORTEST_LENGTH_LTE: Int + id: StringScalarAggregationFilters + id_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { eq: ... } } }' instead.\\") + id_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { gt: ... } } }' instead.\\") + id_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { gte: ... } } }' instead.\\") + id_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { lt: ... } } }' instead.\\") + id_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { averageLength: { lte: ... } } }' instead.\\") + id_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { eq: ... } } }' instead.\\") + id_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { gt: ... } } }' instead.\\") + id_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { gte: ... } } }' instead.\\") + id_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { lt: ... } } }' instead.\\") + id_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { longestLength: { lte: ... } } }' instead.\\") + id_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { eq: ... } } }' instead.\\") + id_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { gt: ... } } }' instead.\\") + id_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { gte: ... } } }' instead.\\") + id_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { lt: ... } } }' instead.\\") + id_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { shortestLength: { lte: ... } } }' instead.\\") } input FriendOfSort { @@ -121,25 +127,39 @@ describe("3817", () => { } input FriendOfUpdateInput { - id: String @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: String + id: StringScalarMutations + id_SET: String @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input FriendOfWhere { AND: [FriendOfWhere!] NOT: FriendOfWhere OR: [FriendOfWhere!] - id: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: String - id_ENDS_WITH: String - id_EQ: String - id_IN: [String] - id_STARTS_WITH: String + id: StringScalarFilters + id_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: String @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Mutation { @@ -163,7 +183,7 @@ describe("3817", () => { } type Person { - friends(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + friends(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! friendsAggregate(where: PersonWhere): PersonPersonFriendsAggregationSelection friendsConnection(after: String, first: Int, sort: [PersonFriendsConnectionSort!], where: PersonFriendsConnectionWhere): PersonFriendsConnection! id: ID! @@ -171,17 +191,12 @@ describe("3817", () => { type PersonAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PersonConnectInput { friends: [PersonFriendsConnectFieldInput!] } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonConnectWhere { node: PersonWhere! } @@ -207,40 +222,45 @@ describe("3817", () => { AND: [PersonFriendsAggregateInput!] NOT: PersonFriendsAggregateInput OR: [PersonFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int edge: FriendOfAggregationWhereInput - node: PersonFriendsNodeAggregationWhereInput } input PersonFriendsConnectFieldInput { connect: [PersonConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PersonConnectWhere } - input PersonFriendsConnectOrCreateFieldInput { - onCreate: PersonFriendsConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input PersonFriendsConnectOrCreateFieldInputOnCreate { - node: PersonOnCreateInput! - } - type PersonFriendsConnection { edges: [PersonFriendsRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input PersonFriendsConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonFriendsConnections match this filter + \\"\\"\\" + all: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where none of the related PersonFriendsConnections match this filter + \\"\\"\\" + none: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where one of the related PersonFriendsConnections match this filter + \\"\\"\\" + single: PersonFriendsConnectionWhere + \\"\\"\\" + Return People where some of the related PersonFriendsConnections match this filter + \\"\\"\\" + some: PersonFriendsConnectionWhere + } + input PersonFriendsConnectionSort { edge: FriendOfSort node: PersonSort @@ -270,26 +290,9 @@ describe("3817", () => { input PersonFriendsFieldInput { connect: [PersonFriendsConnectFieldInput!] - connectOrCreate: [PersonFriendsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [PersonFriendsCreateFieldInput!] } - input PersonFriendsNodeAggregationWhereInput { - AND: [PersonFriendsNodeAggregationWhereInput!] - NOT: PersonFriendsNodeAggregationWhereInput - OR: [PersonFriendsNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type PersonFriendsRelationship { cursor: String! node: Person! @@ -303,7 +306,6 @@ describe("3817", () => { input PersonFriendsUpdateFieldInput { connect: [PersonFriendsConnectFieldInput!] - connectOrCreate: [PersonFriendsConnectOrCreateFieldInput!] create: [PersonFriendsCreateFieldInput!] delete: [PersonFriendsDeleteFieldInput!] disconnect: [PersonFriendsDisconnectFieldInput!] @@ -311,34 +313,24 @@ describe("3817", () => { where: PersonFriendsConnectionWhere } - input PersonOnCreateInput { - \\"\\"\\" - Appears because this input type would be empty otherwise because this type is composed of just generated and/or relationship properties. See https://neo4j.com/docs/graphql-manual/current/troubleshooting/faqs/ - \\"\\"\\" - _emptyInput: Boolean - } - - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - type PersonPersonFriendsAggregationSelection { count: Int! edge: PersonPersonFriendsEdgeAggregateSelection - node: PersonPersonFriendsNodeAggregateSelection } type PersonPersonFriendsEdgeAggregateSelection { id: StringAggregateSelection! } - type PersonPersonFriendsNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -348,11 +340,6 @@ describe("3817", () => { id: SortDirection } - input PersonUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input PersonUpdateInput { friends: [PersonFriendsUpdateFieldInput!] } @@ -361,41 +348,43 @@ describe("3817", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] + friends: PersonRelationshipFilters friendsAggregate: PersonFriendsAggregateInput + friendsConnection: PersonFriendsConnectionFilters \\"\\"\\" Return People where all of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_ALL: PersonFriendsConnectionWhere + friendsConnection_ALL: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_NONE: PersonFriendsConnectionWhere + friendsConnection_NONE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SINGLE: PersonFriendsConnectionWhere + friendsConnection_SINGLE: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonFriendsConnections match this filter \\"\\"\\" - friendsConnection_SOME: PersonFriendsConnectionWhere + friendsConnection_SOME: PersonFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related People match this filter\\"\\"\\" - friends_ALL: PersonWhere + friends_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related People match this filter\\"\\"\\" - friends_NONE: PersonWhere + friends_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related People match this filter\\"\\"\\" - friends_SINGLE: PersonWhere + friends_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related People match this filter\\"\\"\\" - friends_SOME: PersonWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID + friends_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type Query { - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -413,6 +402,27 @@ describe("3817", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/issues/3905.test.ts b/packages/graphql/tests/schema/issues/3905.test.ts index e118f7151f..39869d4606 100644 --- a/packages/graphql/tests/schema/issues/3905.test.ts +++ b/packages/graphql/tests/schema/issues/3905.test.ts @@ -21,8 +21,9 @@ import { printSchemaWithDirectives } from "@graphql-tools/utils"; import { gql } from "graphql-tag"; import { lexicographicSortSchema } from "graphql/utilities"; import { Neo4jGraphQL } from "../../../src"; - -describe("https://github.com/neo4j/graphql/issues/3905", () => { +// This has to be reintroduced when user defined types are supported as target for cypher fields +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("https://github.com/neo4j/graphql/issues/3905", () => { test("custom Cypher result type with list of lists generates without error", async () => { const typeDefs = gql` type Query { @@ -36,11 +37,11 @@ describe("https://github.com/neo4j/graphql/issues/3905", () => { ) } - type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) @node { + type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { paths: [[pathLink]] } - type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) @node { + type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { entity_id: Int other_entity_id: Int } diff --git a/packages/graphql/tests/schema/issues/4511.test.ts b/packages/graphql/tests/schema/issues/4511.test.ts index 315952e867..cad5e0f45c 100644 --- a/packages/graphql/tests/schema/issues/4511.test.ts +++ b/packages/graphql/tests/schema/issues/4511.test.ts @@ -28,24 +28,24 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { const typeDefs = gql` type Movie implements Production @subscription(events: []) @node { title: String! - id: ID @unique - director: Creature! @relationship(type: "DIRECTED", direction: IN) + id: ID + director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } type Series implements Production @node { title: String! episode: Int! - id: ID @unique - director: Creature! @relationship(type: "DIRECTED", direction: IN) + id: ID + director: [Creature!]! @relationship(type: "DIRECTED", direction: IN) } interface Production { id: ID - director: Creature! @declareRelationship + director: [Creature!]! @declareRelationship } type Person implements Creature @node { - movies: Production! @relationship(type: "DIRECTED", direction: OUT) + movies: [Production!]! @relationship(type: "DIRECTED", direction: OUT) } interface Creature { - movies: Production! @declareRelationship + movies: [Production!]! @declareRelationship } `; @@ -88,7 +88,7 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } interface Creature { - movies(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! + movies(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! moviesConnection(after: String, first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! } @@ -97,7 +97,7 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input CreatureConnectInput { - movies: CreatureMoviesConnectFieldInput + movies: [CreatureMoviesConnectFieldInput!] } input CreatureConnectWhere { @@ -109,11 +109,11 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input CreatureDeleteInput { - movies: CreatureMoviesDeleteFieldInput + movies: [CreatureMoviesDeleteFieldInput!] } input CreatureDisconnectInput { - movies: CreatureMoviesDisconnectFieldInput + movies: [CreatureMoviesDisconnectFieldInput!] } type CreatureEdge { @@ -129,13 +129,12 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [CreatureMoviesAggregateInput!] NOT: CreatureMoviesAggregateInput OR: [CreatureMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: CreatureMoviesNodeAggregationWhereInput } input CreatureMoviesConnectFieldInput { @@ -149,6 +148,25 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { totalCount: Int! } + input CreatureMoviesConnectionFilters { + \\"\\"\\" + Return Creatures where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + all: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + none: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + single: CreatureMoviesConnectionWhere + \\"\\"\\" + Return Creatures where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + some: CreatureMoviesConnectionWhere + } + input CreatureMoviesConnectionSort { node: ProductionSort } @@ -174,22 +192,6 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { where: CreatureMoviesConnectionWhere } - input CreatureMoviesNodeAggregationWhereInput { - AND: [CreatureMoviesNodeAggregationWhereInput!] - NOT: CreatureMoviesNodeAggregationWhereInput - OR: [CreatureMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - type CreatureMoviesRelationship { cursor: String! node: Production! @@ -200,32 +202,69 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input CreatureMoviesUpdateFieldInput { - connect: CreatureMoviesConnectFieldInput - create: CreatureMoviesCreateFieldInput - delete: CreatureMoviesDeleteFieldInput - disconnect: CreatureMoviesDisconnectFieldInput + connect: [CreatureMoviesConnectFieldInput!] + create: [CreatureMoviesCreateFieldInput!] + delete: [CreatureMoviesDeleteFieldInput!] + disconnect: [CreatureMoviesDisconnectFieldInput!] update: CreatureMoviesUpdateConnectionInput where: CreatureMoviesConnectionWhere } - input CreatureOptions { - limit: Int - offset: Int + input CreatureRelationshipFilters { + \\"\\"\\"Filter type where all of the related Creatures match this filter\\"\\"\\" + all: CreatureWhere + \\"\\"\\"Filter type where none of the related Creatures match this filter\\"\\"\\" + none: CreatureWhere + \\"\\"\\"Filter type where one of the related Creatures match this filter\\"\\"\\" + single: CreatureWhere + \\"\\"\\"Filter type where some of the related Creatures match this filter\\"\\"\\" + some: CreatureWhere } input CreatureUpdateInput { - movies: CreatureMoviesUpdateFieldInput + movies: [CreatureMoviesUpdateFieldInput!] } input CreatureWhere { AND: [CreatureWhere!] NOT: CreatureWhere OR: [CreatureWhere!] - movies: ProductionWhere + movies: ProductionRelationshipFilters moviesAggregate: CreatureMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere + moviesConnection: CreatureMoviesConnectionFilters + \\"\\"\\" + Return Creatures where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Creatures where all of the related Productions match this filter + \\"\\"\\" + movies_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\" + Return Creatures where none of the related Productions match this filter + \\"\\"\\" + movies_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\" + Return Creatures where one of the related Productions match this filter + \\"\\"\\" + movies_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\" + Return Creatures where some of the related Productions match this filter + \\"\\"\\" + movies_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") typename: [CreatureImplementation!] - typename_IN: [CreatureImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type CreaturesConnection { @@ -250,9 +289,18 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { UPDATE } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -262,17 +310,33 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): MovieCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! + director(limit: Int, offset: Int, where: CreatureWhere): [Creature!]! + directorAggregate(where: CreatureWhere): MovieCreatureDirectorAggregationSelection + directorConnection(after: String, first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! id: ID title: String! } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -287,14 +351,14 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input MovieDeleteInput { - director: MovieDirectorDeleteFieldInput + director: [MovieDirectorDeleteFieldInput!] } input MovieDirectorAggregateInput { AND: [MovieDirectorAggregateInput!] NOT: MovieDirectorAggregateInput OR: [MovieDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -307,6 +371,25 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { where: CreatureConnectWhere } + input MovieDirectorConnectionFilters { + \\"\\"\\" + Return Movies where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Movies where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input MovieDirectorCreateFieldInput { node: CreatureCreateInput! } @@ -322,8 +405,8 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input MovieDirectorFieldInput { - connect: MovieDirectorConnectFieldInput - create: MovieDirectorCreateFieldInput + connect: [MovieDirectorConnectFieldInput!] + create: [MovieDirectorCreateFieldInput!] } input MovieDirectorUpdateConnectionInput { @@ -331,10 +414,10 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input MovieDirectorUpdateFieldInput { - connect: MovieDirectorConnectFieldInput - create: MovieDirectorCreateFieldInput - delete: MovieDirectorDeleteFieldInput - disconnect: MovieDirectorDisconnectFieldInput + connect: [MovieDirectorConnectFieldInput!] + create: [MovieDirectorCreateFieldInput!] + delete: [MovieDirectorDeleteFieldInput!] + disconnect: [MovieDirectorDisconnectFieldInput!] update: MovieDirectorUpdateConnectionInput where: ProductionDirectorConnectionWhere } @@ -344,15 +427,6 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -362,32 +436,56 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input MovieUpdateInput { - director: MovieDirectorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + director: [MovieDirectorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - director: CreatureWhere + director: CreatureRelationshipFilters directorAggregate: MovieDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + directorConnection: MovieDirectorConnectionFilters + \\"\\"\\" + Return Movies where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Creatures match this filter\\"\\"\\" + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Creatures match this filter\\"\\"\\" + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Creatures match this filter\\"\\"\\" + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Creatures match this filter\\"\\"\\" + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -423,9 +521,9 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } type Person implements Creature { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): PersonProductionMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! + movies(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + moviesAggregate(where: ProductionWhere): PersonProductionMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! } type PersonAggregateSelection { @@ -442,7 +540,7 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input PersonDeleteInput { - movies: PersonMoviesDeleteFieldInput + movies: [PersonMoviesDeleteFieldInput!] } type PersonDeletedEvent { @@ -459,13 +557,12 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [PersonMoviesAggregateInput!] NOT: PersonMoviesAggregateInput OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int count_LT: Int count_LTE: Int - node: PersonMoviesNodeAggregationWhereInput } input PersonMoviesConnectFieldInput { @@ -473,6 +570,25 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { where: ProductionConnectWhere } + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + all: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + none: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + single: CreatureMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + some: CreatureMoviesConnectionWhere + } + input PersonMoviesCreateFieldInput { node: ProductionCreateInput! } @@ -488,24 +604,8 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input PersonMoviesFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - } - - input PersonMoviesNodeAggregationWhereInput { - AND: [PersonMoviesNodeAggregationWhereInput!] - NOT: PersonMoviesNodeAggregationWhereInput - OR: [PersonMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + connect: [PersonMoviesConnectFieldInput!] + create: [PersonMoviesCreateFieldInput!] } input PersonMoviesUpdateConnectionInput { @@ -513,30 +613,20 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input PersonMoviesUpdateFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - delete: PersonMoviesDeleteFieldInput - disconnect: PersonMoviesDisconnectFieldInput + connect: [PersonMoviesConnectFieldInput!] + create: [PersonMoviesCreateFieldInput!] + delete: [PersonMoviesDeleteFieldInput!] + disconnect: [PersonMoviesDisconnectFieldInput!] update: PersonMoviesUpdateConnectionInput where: CreatureMoviesConnectionWhere } - input PersonOptions { - limit: Int - offset: Int - } - type PersonProductionMoviesAggregationSelection { count: Int! - node: PersonProductionMoviesNodeAggregateSelection - } - - type PersonProductionMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PersonUpdateInput { - movies: PersonMoviesUpdateFieldInput + movies: [PersonMoviesUpdateFieldInput!] } type PersonUpdatedEvent { @@ -548,24 +638,47 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - movies: ProductionWhere + movies: ProductionRelationshipFilters moviesAggregate: PersonMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere + moviesConnection: PersonMoviesConnectionFilters + \\"\\"\\" + Return People where all of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where none of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where one of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where some of the related CreatureMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: CreatureMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return People where all of the related Productions match this filter\\"\\"\\" + movies_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return People where none of the related Productions match this filter\\"\\"\\" + movies_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return People where one of the related Productions match this filter\\"\\"\\" + movies_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return People where some of the related Productions match this filter\\"\\"\\" + movies_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } interface Production { - director(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! + director(limit: Int, offset: Int, where: CreatureWhere): [Creature!]! directorConnection(after: String, first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! id: ID } type ProductionAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ProductionConnectInput { - director: ProductionDirectorConnectFieldInput + director: [ProductionDirectorConnectFieldInput!] } input ProductionConnectWhere { @@ -578,14 +691,14 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input ProductionDeleteInput { - director: ProductionDirectorDeleteFieldInput + director: [ProductionDirectorDeleteFieldInput!] } input ProductionDirectorAggregateInput { AND: [ProductionDirectorAggregateInput!] NOT: ProductionDirectorAggregateInput OR: [ProductionDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -604,6 +717,25 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { totalCount: Int! } + input ProductionDirectorConnectionFilters { + \\"\\"\\" + Return Productions where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Productions where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input ProductionDirectorConnectionWhere { AND: [ProductionDirectorConnectionWhere!] NOT: ProductionDirectorConnectionWhere @@ -635,16 +767,16 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input ProductionDirectorUpdateFieldInput { - connect: ProductionDirectorConnectFieldInput - create: ProductionDirectorCreateFieldInput - delete: ProductionDirectorDeleteFieldInput - disconnect: ProductionDirectorDisconnectFieldInput + connect: [ProductionDirectorConnectFieldInput!] + create: [ProductionDirectorCreateFieldInput!] + delete: [ProductionDirectorDeleteFieldInput!] + disconnect: [ProductionDirectorDisconnectFieldInput!] update: ProductionDirectorUpdateConnectionInput where: ProductionDirectorConnectionWhere } input ProductionDisconnectInput { - director: ProductionDirectorDisconnectFieldInput + director: [ProductionDirectorDisconnectFieldInput!] } type ProductionEdge { @@ -657,13 +789,15 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { Series } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -674,26 +808,57 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input ProductionUpdateInput { - director: ProductionDirectorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + director: [ProductionDirectorUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input ProductionWhere { AND: [ProductionWhere!] NOT: ProductionWhere OR: [ProductionWhere!] - director: CreatureWhere + director: CreatureRelationshipFilters directorAggregate: ProductionDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + directorConnection: ProductionDirectorConnectionFilters + \\"\\"\\" + Return Productions where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Productions where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Productions where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Productions where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Productions where all of the related Creatures match this filter + \\"\\"\\" + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") + \\"\\"\\" + Return Productions where none of the related Creatures match this filter + \\"\\"\\" + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") + \\"\\"\\" + Return Productions where one of the related Creatures match this filter + \\"\\"\\" + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") + \\"\\"\\" + Return Productions where some of the related Creatures match this filter + \\"\\"\\" + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ProductionsConnection { @@ -703,27 +868,27 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } type Query { - creatures(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): [Creature!]! + creatures(limit: Int, offset: Int, where: CreatureWhere): [Creature!]! creaturesAggregate(where: CreatureWhere): CreatureAggregateSelection! creaturesConnection(after: String, first: Int, where: CreatureWhere): CreaturesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, where: PersonWhere): PeopleConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! } type Series implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): SeriesCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! + director(limit: Int, offset: Int, where: CreatureWhere): [Creature!]! + directorAggregate(where: CreatureWhere): SeriesCreatureDirectorAggregationSelection + directorConnection(after: String, first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! episode: Int! id: ID title: String! @@ -732,7 +897,6 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { type SeriesAggregateSelection { count: Int! episode: IntAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -760,7 +924,7 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input SeriesDeleteInput { - director: SeriesDirectorDeleteFieldInput + director: [SeriesDirectorDeleteFieldInput!] } type SeriesDeletedEvent { @@ -773,7 +937,7 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [SeriesDirectorAggregateInput!] NOT: SeriesDirectorAggregateInput OR: [SeriesDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -786,6 +950,25 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { where: CreatureConnectWhere } + input SeriesDirectorConnectionFilters { + \\"\\"\\" + Return Series where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + all: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + none: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + single: ProductionDirectorConnectionWhere + \\"\\"\\" + Return Series where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + some: ProductionDirectorConnectionWhere + } + input SeriesDirectorCreateFieldInput { node: CreatureCreateInput! } @@ -801,8 +984,8 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input SeriesDirectorFieldInput { - connect: SeriesDirectorConnectFieldInput - create: SeriesDirectorCreateFieldInput + connect: [SeriesDirectorConnectFieldInput!] + create: [SeriesDirectorCreateFieldInput!] } input SeriesDirectorUpdateConnectionInput { @@ -810,10 +993,10 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { } input SeriesDirectorUpdateFieldInput { - connect: SeriesDirectorConnectFieldInput - create: SeriesDirectorCreateFieldInput - delete: SeriesDirectorDeleteFieldInput - disconnect: SeriesDirectorDisconnectFieldInput + connect: [SeriesDirectorConnectFieldInput!] + create: [SeriesDirectorCreateFieldInput!] + delete: [SeriesDirectorDeleteFieldInput!] + disconnect: [SeriesDirectorDisconnectFieldInput!] update: SeriesDirectorUpdateConnectionInput where: ProductionDirectorConnectionWhere } @@ -829,15 +1012,6 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { title: String! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -851,37 +1025,37 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [SeriesSubscriptionWhere!] NOT: SeriesSubscriptionWhere OR: [SeriesSubscriptionWhere!] - episode: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episode_EQ: Int - episode_GT: Int - episode_GTE: Int - episode_IN: [Int!] - episode_LT: Int - episode_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + episode: IntScalarFilters + episode_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { eq: ... }\\") + episode_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gt: ... }\\") + episode_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gte: ... }\\") + episode_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episode: { in: ... }\\") + episode_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lt: ... }\\") + episode_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } input SeriesUpdateInput { - director: SeriesDirectorUpdateFieldInput - episode: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episode_DECREMENT: Int - episode_INCREMENT: Int - episode_SET: Int - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + director: [SeriesDirectorUpdateFieldInput!] + episode: IntScalarMutations + episode_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episode: { decrement: ... } }' instead.\\") + episode_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episode: { increment: ... } }' instead.\\") + episode_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episode: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } type SeriesUpdatedEvent { @@ -895,28 +1069,52 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] - director: CreatureWhere + director: CreatureRelationshipFilters directorAggregate: SeriesDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - episode: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episode_EQ: Int - episode_GT: Int - episode_GTE: Int - episode_IN: [Int!] - episode_LT: Int - episode_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + directorConnection: SeriesDirectorConnectionFilters + \\"\\"\\" + Return Series where all of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_ALL: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Series where none of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_NONE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Series where one of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SINGLE: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Series where some of the related ProductionDirectorConnections match this filter + \\"\\"\\" + directorConnection_SOME: ProductionDirectorConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Series where all of the related Creatures match this filter\\"\\"\\" + director_ALL: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { all: ... }' instead.\\") + \\"\\"\\"Return Series where none of the related Creatures match this filter\\"\\"\\" + director_NONE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { none: ... }' instead.\\") + \\"\\"\\"Return Series where one of the related Creatures match this filter\\"\\"\\" + director_SINGLE: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { single: ... }' instead.\\") + \\"\\"\\"Return Series where some of the related Creatures match this filter\\"\\"\\" + director_SOME: CreatureWhere @deprecated(reason: \\"Please use the relevant generic filter 'director: { some: ... }' instead.\\") + episode: IntScalarFilters + episode_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { eq: ... }\\") + episode_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gt: ... }\\") + episode_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { gte: ... }\\") + episode_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episode: { in: ... }\\") + episode_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lt: ... }\\") + episode_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episode: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -932,6 +1130,20 @@ describe("https://github.com/neo4j/graphql/issues/4511", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { personCreated: PersonCreatedEvent! personDeleted: PersonDeletedEvent! diff --git a/packages/graphql/tests/schema/issues/4615.test.ts b/packages/graphql/tests/schema/issues/4615.test.ts index eb03d11b3b..eef4d96968 100644 --- a/packages/graphql/tests/schema/issues/4615.test.ts +++ b/packages/graphql/tests/schema/issues/4615.test.ts @@ -77,26 +77,27 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -108,29 +109,29 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ShowOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ShowSort!], where: ShowWhere): [Show!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ShowWhere): ActorShowActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [ShowSort!], where: ShowWhere): [Show!]! + actedInAggregate(where: ShowWhere): ActorShowActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -138,7 +139,7 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -160,6 +161,25 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: ShowSort @@ -197,21 +217,22 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -265,13 +286,15 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } type ActorShowActedInAggregationSelection { @@ -297,45 +320,47 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: ShowRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Shows match this filter\\"\\"\\" - actedIn_ALL: ShowWhere + actedIn_ALL: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Shows match this filter\\"\\"\\" - actedIn_NONE: ShowWhere + actedIn_NONE: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Shows match this filter\\"\\"\\" - actedIn_SINGLE: ShowWhere + actedIn_SINGLE: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Shows match this filter\\"\\"\\" - actedIn_SOME: ShowWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: ShowWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -375,6 +400,21 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { min: DateTime } + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -383,6 +423,16 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -390,10 +440,35 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Show { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! release: DateTime! runtime: Int title: String! @@ -417,7 +492,7 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -430,11 +505,26 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput + where: ActorConnectWhere + } + + input MovieActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Movies where all of the related ShowActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere } input MovieActorsCreateFieldInput { @@ -451,21 +541,22 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input MovieActorsUpdateConnectionInput { @@ -505,15 +596,6 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -525,65 +607,67 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - release: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - release_SET: DateTime - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + release: DateTimeScalarMutations + release_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'release: { set: ... } }' instead.\\") + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - release: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - release_EQ: DateTime - release_GT: DateTime - release_GTE: DateTime - release_IN: [DateTime!] - release_LT: DateTime - release_LTE: DateTime - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + release: DateTimeScalarFilters + release_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter release: { eq: ... }\\") + release_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter release: { gt: ... }\\") + release_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter release: { gte: ... }\\") + release_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter release: { in: ... }\\") + release_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter release: { lt: ... }\\") + release_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter release: { lte: ... }\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -613,24 +697,24 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! + series(limit: Int, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - shows(limit: Int, offset: Int, options: ShowOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ShowSort!], where: ShowWhere): [Show!]! + shows(limit: Int, offset: Int, sort: [ShowSort!], where: ShowWhere): [Show!]! showsAggregate(where: ShowWhere): ShowAggregateSelection! showsConnection(after: String, first: Int, sort: [ShowSort!], where: ShowWhere): ShowsConnection! } type Series implements Show { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): SeriesActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): SeriesActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! episodes: Int title: String! } @@ -653,7 +737,7 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [SeriesActorsAggregateInput!] NOT: SeriesActorsAggregateInput OR: [SeriesActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -666,11 +750,26 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input SeriesActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput + where: ActorConnectWhere + } + + input SeriesActorsConnectionFilters { \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. + Return Series where all of the related ShowActorsConnections match this filter \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Series where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere } input SeriesActorsCreateFieldInput { @@ -687,21 +786,22 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [SeriesActorsNodeAggregationWhereInput!] NOT: SeriesActorsNodeAggregationWhereInput OR: [SeriesActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } input SeriesActorsUpdateConnectionInput { @@ -745,15 +845,6 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { node: Series! } - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - \\"\\"\\" Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. \\"\\"\\" @@ -764,60 +855,62 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input SeriesUpdateInput { actors: [SeriesActorsUpdateFieldInput!] - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input SeriesWhere { AND: [SeriesWhere!] NOT: SeriesWhere OR: [SeriesWhere!] + actors: ActorRelationshipFilters actorsAggregate: SeriesActorsAggregateInput + actorsConnection: SeriesActorsConnectionFilters \\"\\"\\" Return Series where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Series where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Series where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Series where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Series where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Series where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } interface Show { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsConnection(after: String, first: Int, sort: [ShowActorsConnectionSort!], where: ShowActorsConnectionWhere): ShowActorsConnection! title: String! } @@ -826,7 +919,7 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [ShowActorsAggregateInput!] NOT: ShowActorsAggregateInput OR: [ShowActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -839,10 +932,6 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input ShowActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ShowActorsEdgeCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -852,6 +941,25 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { totalCount: Int! } + input ShowActorsConnectionFilters { + \\"\\"\\" + Return Shows where all of the related ShowActorsConnections match this filter + \\"\\"\\" + all: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where none of the related ShowActorsConnections match this filter + \\"\\"\\" + none: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where one of the related ShowActorsConnections match this filter + \\"\\"\\" + single: ShowActorsConnectionWhere + \\"\\"\\" + Return Shows where some of the related ShowActorsConnections match this filter + \\"\\"\\" + some: ShowActorsConnectionWhere + } + input ShowActorsConnectionSort { edge: ShowActorsEdgeSort node: ActorSort @@ -929,21 +1037,22 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { AND: [ShowActorsNodeAggregationWhereInput!] NOT: ShowActorsNodeAggregationWhereInput OR: [ShowActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type ShowActorsRelationship { @@ -1004,13 +1113,15 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { Series } - input ShowOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ShowSort objects to sort Shows by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ShowSort!] + input ShowRelationshipFilters { + \\"\\"\\"Filter type where all of the related Shows match this filter\\"\\"\\" + all: ShowWhere + \\"\\"\\"Filter type where none of the related Shows match this filter\\"\\"\\" + none: ShowWhere + \\"\\"\\"Filter type where one of the related Shows match this filter\\"\\"\\" + single: ShowWhere + \\"\\"\\"Filter type where some of the related Shows match this filter\\"\\"\\" + some: ShowWhere } \\"\\"\\" @@ -1022,47 +1133,48 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { input ShowUpdateInput { actors: [ShowActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input ShowWhere { AND: [ShowWhere!] NOT: ShowWhere OR: [ShowWhere!] + actors: ActorRelationshipFilters actorsAggregate: ShowActorsAggregateInput + actorsConnection: ShowActorsConnectionFilters \\"\\"\\" Return Shows where all of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: ShowActorsConnectionWhere + actorsConnection_ALL: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where none of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: ShowActorsConnectionWhere + actorsConnection_NONE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where one of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: ShowActorsConnectionWhere + actorsConnection_SINGLE: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Shows where some of the related ShowActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: ShowActorsConnectionWhere + actorsConnection_SOME: ShowActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Shows where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Shows where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Shows where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Shows where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") typename: [ShowImplementation!] - typename_IN: [ShowImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ShowsConnection { @@ -1084,6 +1196,27 @@ describe("https://github.com/neo4j/graphql/issues/4615", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/5428.test.ts b/packages/graphql/tests/schema/issues/5428.test.ts index a25c614f7e..1530c0b067 100644 --- a/packages/graphql/tests/schema/issues/5428.test.ts +++ b/packages/graphql/tests/schema/issues/5428.test.ts @@ -76,7 +76,7 @@ describe("https://github.com/neo4j/graphql/issues/5428", () => { } type Query { - test(limit: Int, offset: Int, options: TestOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [TestSort!], where: TestWhere): [Test!]! + test(limit: Int, offset: Int, sort: [TestSort!], where: TestWhere): [Test!]! testAggregate(where: TestWhere): TestAggregateSelection! testConnection(after: String, first: Int, sort: [TestSort!], where: TestWhere): TestConnection! } @@ -94,6 +94,20 @@ describe("https://github.com/neo4j/graphql/issues/5428", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Test { Name: String } @@ -118,15 +132,6 @@ describe("https://github.com/neo4j/graphql/issues/5428", () => { node: Test! } - input TestOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more TestSort objects to sort Test by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [TestSort!] - } - \\"\\"\\" Fields to sort Test by. The order in which sorts are applied is not guaranteed when specifying many fields in one TestSort object. \\"\\"\\" @@ -135,19 +140,19 @@ describe("https://github.com/neo4j/graphql/issues/5428", () => { } input TestUpdateInput { - Name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - Name_SET: String + Name: StringScalarMutations + Name_SET: String @deprecated(reason: \\"Please use the generic mutation 'Name: { set: ... } }' instead.\\") } input TestWhere { AND: [TestWhere!] NOT: TestWhere - Name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - Name_CONTAINS: String - Name_ENDS_WITH: String - Name_EQ: String - Name_IN: [String] - Name_STARTS_WITH: String + Name: StringScalarFilters + Name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter Name: { contains: ... }\\") + Name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter Name: { endsWith: ... }\\") + Name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter Name: { eq: ... }\\") + Name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter Name: { in: ... }\\") + Name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter Name: { startsWith: ... }\\") OR: [TestWhere!] } diff --git a/packages/graphql/tests/schema/issues/556.test.ts b/packages/graphql/tests/schema/issues/556.test.ts index db0875e5e4..b7e1669944 100644 --- a/packages/graphql/tests/schema/issues/556.test.ts +++ b/packages/graphql/tests/schema/issues/556.test.ts @@ -33,7 +33,7 @@ describe("https://github.com/neo4j/graphql/issues/556", () => { } type Article @node { - id: ID! @id @unique + id: ID! @id blocks: [Block!]! @relationship(type: "HAS_BLOCK", direction: OUT, properties: "HasBlock") images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } @@ -47,16 +47,16 @@ describe("https://github.com/neo4j/graphql/issues/556", () => { } type TextBlock implements Block @node { - id: ID @id @unique + id: ID @id text: String } type DividerBlock implements Block @node { - id: ID @id @unique + id: ID @id } type ImageBlock implements Block @node { - id: ID @id @unique + id: ID @id images: [Image!]! @relationship(type: "HAS_IMAGE", direction: OUT) } diff --git a/packages/graphql/tests/schema/issues/5631.test.ts b/packages/graphql/tests/schema/issues/5631.test.ts index 6de7cbd1fc..d1b3404d57 100644 --- a/packages/graphql/tests/schema/issues/5631.test.ts +++ b/packages/graphql/tests/schema/issues/5631.test.ts @@ -79,15 +79,6 @@ describe("https://github.com/neo4j/graphql/issues/5631", () => { node: Actor! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -107,12 +98,12 @@ describe("https://github.com/neo4j/graphql/issues/5631", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - custom_string_with_zero_param: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - custom_string_with_zero_param_CONTAINS: String - custom_string_with_zero_param_ENDS_WITH: String - custom_string_with_zero_param_EQ: String - custom_string_with_zero_param_IN: [String!] - custom_string_with_zero_param_STARTS_WITH: String + custom_string_with_zero_param: StringScalarFilters + custom_string_with_zero_param_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter custom_string_with_zero_param: { contains: ... }\\") + custom_string_with_zero_param_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_string_with_zero_param: { endsWith: ... }\\") + custom_string_with_zero_param_EQ: String @deprecated(reason: \\"Please use the relevant generic filter custom_string_with_zero_param: { eq: ... }\\") + custom_string_with_zero_param_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter custom_string_with_zero_param: { in: ... }\\") + custom_string_with_zero_param_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter custom_string_with_zero_param: { startsWith: ... }\\") } type ActorsConnection { @@ -169,11 +160,6 @@ describe("https://github.com/neo4j/graphql/issues/5631", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - } - input MovieUpdateInput { \\"\\"\\" Appears because this input type would be empty otherwise because this type is composed of just generated and/or relationship properties. See https://neo4j.com/docs/graphql-manual/current/troubleshooting/faqs/ @@ -212,10 +198,10 @@ describe("https://github.com/neo4j/graphql/issues/5631", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, where: MovieWhere): MoviesConnection! } @@ -228,6 +214,15 @@ describe("https://github.com/neo4j/graphql/issues/5631", () => { DESC } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/609.test.ts b/packages/graphql/tests/schema/issues/609.test.ts index 6af0e722cd..17397109b5 100644 --- a/packages/graphql/tests/schema/issues/609.test.ts +++ b/packages/graphql/tests/schema/issues/609.test.ts @@ -77,24 +77,15 @@ describe("609", () => { node: Deprecated! } - input DeprecatedOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more DeprecatedSort objects to sort Deprecateds by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [DeprecatedSort!] - } - \\"\\"\\" Fields to sort Deprecateds by. The order in which sorts are applied is not guaranteed when specifying many fields in one DeprecatedSort object. \\"\\"\\" input DeprecatedSort { - deprecatedField: SortDirection @deprecated + deprecatedField: SortDirection } input DeprecatedUpdateInput { - deprecatedField: String @deprecated + deprecatedField: StringScalarMutations @deprecated deprecatedField_SET: String @deprecated } @@ -102,7 +93,7 @@ describe("609", () => { AND: [DeprecatedWhere!] NOT: DeprecatedWhere OR: [DeprecatedWhere!] - deprecatedField: String @deprecated + deprecatedField: StringScalarFilters @deprecated deprecatedField_CONTAINS: String @deprecated deprecatedField_ENDS_WITH: String @deprecated deprecatedField_EQ: String @deprecated @@ -131,7 +122,7 @@ describe("609", () => { } type Query { - deprecateds(limit: Int, offset: Int, options: DeprecatedOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [DeprecatedSort!], where: DeprecatedWhere): [Deprecated!]! + deprecateds(limit: Int, offset: Int, sort: [DeprecatedSort!], where: DeprecatedWhere): [Deprecated!]! deprecatedsAggregate(where: DeprecatedWhere): DeprecatedAggregateSelection! deprecatedsConnection(after: String, first: Int, sort: [DeprecatedSort!], where: DeprecatedWhere): DeprecatedsConnection! } @@ -149,6 +140,20 @@ describe("609", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateDeprecatedsMutationResponse { deprecateds: [Deprecated!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/872.test.ts b/packages/graphql/tests/schema/issues/872.test.ts index b0aaf37f45..149907ae26 100644 --- a/packages/graphql/tests/schema/issues/872.test.ts +++ b/packages/graphql/tests/schema/issues/872.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { const typeDefs = gql` type Movie @node { title: String! - id: ID! @id @unique + id: ID! @id } type Actor @node { @@ -50,16 +50,16 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } type Actor2 { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): Actor2MovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [Actor2MoviesConnectionSort!], where: Actor2MoviesConnectionWhere): Actor2MoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): Actor2MovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [Actor2MoviesConnectionSort!], where: Actor2MoviesConnectionWhere): Actor2MoviesConnection! name: String! } @@ -88,7 +88,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } type Actor2MovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -96,7 +95,7 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { AND: [Actor2MoviesAggregateInput!] NOT: Actor2MoviesAggregateInput OR: [Actor2MoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -106,28 +105,34 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } input Actor2MoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } - input Actor2MoviesConnectOrCreateFieldInput { - onCreate: Actor2MoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input Actor2MoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - type Actor2MoviesConnection { edges: [Actor2MoviesRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input Actor2MoviesConnectionFilters { + \\"\\"\\" + Return Actor2s where all of the related Actor2MoviesConnections match this filter + \\"\\"\\" + all: Actor2MoviesConnectionWhere + \\"\\"\\" + Return Actor2s where none of the related Actor2MoviesConnections match this filter + \\"\\"\\" + none: Actor2MoviesConnectionWhere + \\"\\"\\" + Return Actor2s where one of the related Actor2MoviesConnections match this filter + \\"\\"\\" + single: Actor2MoviesConnectionWhere + \\"\\"\\" + Return Actor2s where some of the related Actor2MoviesConnections match this filter + \\"\\"\\" + some: Actor2MoviesConnectionWhere + } + input Actor2MoviesConnectionSort { node: MovieSort } @@ -153,7 +158,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input Actor2MoviesFieldInput { connect: [Actor2MoviesConnectFieldInput!] - connectOrCreate: [Actor2MoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [Actor2MoviesCreateFieldInput!] } @@ -161,31 +165,22 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { AND: [Actor2MoviesNodeAggregationWhereInput!] NOT: Actor2MoviesNodeAggregationWhereInput OR: [Actor2MoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type Actor2MoviesRelationship { @@ -199,7 +194,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input Actor2MoviesUpdateFieldInput { connect: [Actor2MoviesConnectFieldInput!] - connectOrCreate: [Actor2MoviesConnectOrCreateFieldInput!] create: [Actor2MoviesCreateFieldInput!] delete: [Actor2MoviesDeleteFieldInput!] disconnect: [Actor2MoviesDisconnectFieldInput!] @@ -207,15 +201,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { where: Actor2MoviesConnectionWhere } - input Actor2Options { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more Actor2Sort objects to sort Actor2s by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [Actor2Sort!] - } - \\"\\"\\" Fields to sort Actor2s by. The order in which sorts are applied is not guaranteed when specifying many fields in one Actor2Sort object. \\"\\"\\" @@ -225,45 +210,47 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input Actor2UpdateInput { movies: [Actor2MoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input Actor2Where { AND: [Actor2Where!] NOT: Actor2Where OR: [Actor2Where!] + movies: MovieRelationshipFilters moviesAggregate: Actor2MoviesAggregateInput + moviesConnection: Actor2MoviesConnectionFilters \\"\\"\\" Return Actor2s where all of the related Actor2MoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: Actor2MoviesConnectionWhere + moviesConnection_ALL: Actor2MoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actor2s where none of the related Actor2MoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: Actor2MoviesConnectionWhere + moviesConnection_NONE: Actor2MoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actor2s where one of the related Actor2MoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: Actor2MoviesConnectionWhere + moviesConnection_SINGLE: Actor2MoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actor2s where some of the related Actor2MoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: Actor2MoviesConnectionWhere + moviesConnection_SOME: Actor2MoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actor2s where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actor2s where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actor2s where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actor2s where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Actor2sConnection { @@ -297,7 +284,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } @@ -305,7 +291,7 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -315,28 +301,34 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - node: MovieOnCreateInput! - } - type ActorMoviesConnection { edges: [ActorMoviesRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -362,7 +354,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input ActorMoviesFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ActorMoviesCreateFieldInput!] } @@ -370,31 +361,22 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -408,7 +390,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input ActorMoviesUpdateFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] create: [ActorMoviesCreateFieldInput!] delete: [ActorMoviesDeleteFieldInput!] disconnect: [ActorMoviesDisconnectFieldInput!] @@ -416,15 +397,6 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -434,45 +406,47 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { input ActorUpdateInput { movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -512,9 +486,33 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int } type Movie { @@ -524,14 +522,9 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") title: StringAggregateSelection! } - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - input MovieConnectWhere { node: MovieWhere! } @@ -545,17 +538,15 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { node: Movie! } - input MovieOnCreateInput { - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -566,32 +557,27 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { title: SortDirection } - input MovieUniqueWhere { - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: ID - } - input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -621,13 +607,13 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { } type Query { - actor2s(limit: Int, offset: Int, options: Actor2Options @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [Actor2Sort!], where: Actor2Where): [Actor2!]! + actor2s(limit: Int, offset: Int, sort: [Actor2Sort!], where: Actor2Where): [Actor2!]! actor2sAggregate(where: Actor2Where): Actor2AggregateSelection! actor2sConnection(after: String, first: Int, sort: [Actor2Sort!], where: Actor2Where): Actor2sConnection! - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -645,6 +631,27 @@ describe("https://github.com/neo4j/graphql/issues/872", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActor2sMutationResponse { actor2s: [Actor2!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/issues/873.test.ts b/packages/graphql/tests/schema/issues/873.test.ts new file mode 100644 index 0000000000..998ad0432a --- /dev/null +++ b/packages/graphql/tests/schema/issues/873.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GraphQLError } from "graphql"; +import { Neo4jGraphQL } from "../../../src"; + +describe("https://github.com/neo4j/graphql/issues/873", () => { + test("Fails on name conflict in schema generation", async () => { + const typeDefs = /* GraphQL */ ` + type Tech @node @plural(value: "Techs") { + name: String + } + + type AnotherTech @node @plural(value: "Techs") { + name: String + } + + type Techs @node { + value: String + } + `; + + await expect(async () => { + const neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + await neoSchema.getSchema(); + }).rejects.toIncludeSameMembers([ + new GraphQLError(`Ambiguous plural "techs" in "AnotherTech"`), + new GraphQLError(`Ambiguous plural "techs" in "Techs"`), + ]); + }); +}); diff --git a/packages/graphql/tests/schema/limit-required.test.ts b/packages/graphql/tests/schema/limit-required.test.ts new file mode 100644 index 0000000000..0ed200d767 --- /dev/null +++ b/packages/graphql/tests/schema/limit-required.test.ts @@ -0,0 +1,4175 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { gql } from "graphql-tag"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../src"; + +describe("limitRequired constructor option", () => { + test("interfaces and interface relationships", async () => { + const typeDefs = gql` + interface Production { + id: ID! + title: String! + } + type Movie implements Production @node { + id: ID! + title: String! + runtime: Int! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + type Series implements Production @node { + id: ID! + title: String! + episodes: Int! + } + interface Person { + id: ID! + name: String! + actedIn: [Production!]! @declareRelationship + movies: [Movie!]! @declareRelationship + } + type Actor implements Person @node { + id: ID! + name: String! + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs, features: { limitRequired: true } }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + The edge properties for the following fields: + * Movie.actors + * Actor.movies + * Actor.actedIn + \\"\\"\\" + type ActedIn { + screenTime: Int! + } + + input ActedInAggregationWhereInput { + AND: [ActedInAggregationWhereInput!] + NOT: ActedInAggregationWhereInput + OR: [ActedInAggregationWhereInput!] + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") + } + + input ActedInCreateInput { + screenTime: Int! + } + + input ActedInSort { + screenTime: SortDirection + } + + input ActedInUpdateInput { + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + } + + input ActedInWhere { + AND: [ActedInWhere!] + NOT: ActedInWhere + OR: [ActedInWhere!] + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + } + + type Actor implements Person { + actedIn(limit: Int!, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int!, sort: [PersonActedInConnectionSort!], where: PersonActedInConnectionWhere): PersonActedInConnection! + id: ID! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int!, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! + name: String! + } + + input ActorActedInAggregateInput { + AND: [ActorActedInAggregateInput!] + NOT: ActorActedInAggregateInput + OR: [ActorActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorActedInNodeAggregationWhereInput + } + + input ActorActedInConnectFieldInput { + edge: ActedInCreateInput! + where: ProductionConnectWhere + } + + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related PersonActedInConnections match this filter + \\"\\"\\" + all: PersonActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related PersonActedInConnections match this filter + \\"\\"\\" + none: PersonActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related PersonActedInConnections match this filter + \\"\\"\\" + single: PersonActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related PersonActedInConnections match this filter + \\"\\"\\" + some: PersonActedInConnectionWhere + } + + input ActorActedInCreateFieldInput { + edge: ActedInCreateInput! + node: ProductionCreateInput! + } + + input ActorActedInDeleteFieldInput { + where: PersonActedInConnectionWhere + } + + input ActorActedInDisconnectFieldInput { + where: PersonActedInConnectionWhere + } + + input ActorActedInFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + } + + input ActorActedInNodeAggregationWhereInput { + AND: [ActorActedInNodeAggregationWhereInput!] + NOT: ActorActedInNodeAggregationWhereInput + OR: [ActorActedInNodeAggregationWhereInput!] + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + input ActorActedInUpdateConnectionInput { + edge: ActedInUpdateInput + node: ProductionUpdateInput + } + + input ActorActedInUpdateFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + delete: [ActorActedInDeleteFieldInput!] + disconnect: [ActorActedInDisconnectFieldInput!] + update: ActorActedInUpdateConnectionInput + where: PersonActedInConnectionWhere + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorConnectInput { + actedIn: [ActorActedInConnectFieldInput!] + movies: [ActorMoviesConnectFieldInput!] + } + + input ActorConnectWhere { + node: ActorWhere! + } + + input ActorCreateInput { + actedIn: ActorActedInFieldInput + id: ID! + movies: ActorMoviesFieldInput + name: String! + } + + input ActorDeleteInput { + actedIn: [ActorActedInDeleteFieldInput!] + movies: [PersonMoviesDeleteFieldInput!] + } + + input ActorDisconnectInput { + actedIn: [ActorActedInDisconnectFieldInput!] + movies: [PersonMoviesDisconnectFieldInput!] + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + type ActorMovieMoviesAggregationSelection { + count: Int! + edge: ActorMovieMoviesEdgeAggregateSelection + node: ActorMovieMoviesNodeAggregateSelection + } + + type ActorMovieMoviesEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorMovieMoviesNodeAggregateSelection { + runtime: IntAggregateSelection! + title: StringAggregateSelection! + } + + input ActorMoviesAggregateInput { + AND: [ActorMoviesAggregateInput!] + NOT: ActorMoviesAggregateInput + OR: [ActorMoviesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorMoviesNodeAggregationWhereInput + } + + input ActorMoviesConnectFieldInput { + connect: [MovieConnectInput!] + edge: ActedInCreateInput! + where: MovieConnectWhere + } + + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + all: PersonMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + none: PersonMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + single: PersonMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + some: PersonMoviesConnectionWhere + } + + input ActorMoviesCreateFieldInput { + edge: ActedInCreateInput! + node: MovieCreateInput! + } + + input ActorMoviesFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + } + + input ActorMoviesNodeAggregationWhereInput { + AND: [ActorMoviesNodeAggregationWhereInput!] + NOT: ActorMoviesNodeAggregationWhereInput + OR: [ActorMoviesNodeAggregationWhereInput!] + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + input ActorMoviesUpdateConnectionInput { + edge: ActedInUpdateInput + node: MovieUpdateInput + } + + input ActorMoviesUpdateFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + delete: [PersonMoviesDeleteFieldInput!] + disconnect: [PersonMoviesDisconnectFieldInput!] + update: ActorMoviesUpdateConnectionInput + where: PersonMoviesConnectionWhere + } + + type ActorProductionActedInAggregationSelection { + count: Int! + edge: ActorProductionActedInEdgeAggregateSelection + node: ActorProductionActedInNodeAggregateSelection + } + + type ActorProductionActedInEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorProductionActedInNodeAggregateSelection { + title: StringAggregateSelection! + } + + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + id: SortDirection + name: SortDirection + } + + input ActorUpdateInput { + actedIn: [ActorActedInUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [ActorMoviesUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters + \\"\\"\\" + Return Actors where all of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_ALL: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_NONE: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_SINGLE: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_SOME: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters + moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters + \\"\\"\\" + Return Actors where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + type CreateSeriesMutationResponse { + info: CreateInfo! + series: [Series!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + type Movie implements Production { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int!, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + id: ID! + runtime: Int! + title: String! + } + + type MovieActorActorsAggregationSelection { + count: Int! + edge: MovieActorActorsEdgeAggregateSelection + node: MovieActorActorsNodeAggregateSelection + } + + type MovieActorActorsEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type MovieActorActorsNodeAggregateSelection { + name: StringAggregateSelection! + } + + input MovieActorsAggregateInput { + AND: [MovieActorsAggregateInput!] + NOT: MovieActorsAggregateInput + OR: [MovieActorsAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: MovieActorsNodeAggregationWhereInput + } + + input MovieActorsConnectFieldInput { + connect: [ActorConnectInput!] + edge: ActedInCreateInput! + where: ActorConnectWhere + } + + type MovieActorsConnection { + edges: [MovieActorsRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + + input MovieActorsConnectionSort { + edge: ActedInSort + node: ActorSort + } + + input MovieActorsConnectionWhere { + AND: [MovieActorsConnectionWhere!] + NOT: MovieActorsConnectionWhere + OR: [MovieActorsConnectionWhere!] + edge: ActedInWhere + node: ActorWhere + } + + input MovieActorsCreateFieldInput { + edge: ActedInCreateInput! + node: ActorCreateInput! + } + + input MovieActorsDeleteFieldInput { + delete: ActorDeleteInput + where: MovieActorsConnectionWhere + } + + input MovieActorsDisconnectFieldInput { + disconnect: ActorDisconnectInput + where: MovieActorsConnectionWhere + } + + input MovieActorsFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + } + + input MovieActorsNodeAggregationWhereInput { + AND: [MovieActorsNodeAggregationWhereInput!] + NOT: MovieActorsNodeAggregationWhereInput + OR: [MovieActorsNodeAggregationWhereInput!] + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + } + + type MovieActorsRelationship { + cursor: String! + node: Actor! + properties: ActedIn! + } + + input MovieActorsUpdateConnectionInput { + edge: ActedInUpdateInput + node: ActorUpdateInput + } + + input MovieActorsUpdateFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + delete: [MovieActorsDeleteFieldInput!] + disconnect: [MovieActorsDisconnectFieldInput!] + update: MovieActorsUpdateConnectionInput + where: MovieActorsConnectionWhere + } + + type MovieAggregateSelection { + count: Int! + runtime: IntAggregateSelection! + title: StringAggregateSelection! + } + + input MovieConnectInput { + actors: [MovieActorsConnectFieldInput!] + } + + input MovieConnectWhere { + node: MovieWhere! + } + + input MovieCreateInput { + actors: MovieActorsFieldInput + id: ID! + runtime: Int! + title: String! + } + + input MovieDeleteInput { + actors: [MovieActorsDeleteFieldInput!] + } + + input MovieDisconnectInput { + actors: [MovieActorsDisconnectFieldInput!] + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + id: SortDirection + runtime: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + actors: [MovieActorsUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + actors: ActorRelationshipFilters + actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! + deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! + deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! + deleteSeries(where: SeriesWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type PeopleConnection { + edges: [PersonEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + interface Person { + actedIn(limit: Int!, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInConnection(after: String, first: Int!, sort: [PersonActedInConnectionSort!], where: PersonActedInConnectionWhere): PersonActedInConnection! + id: ID! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesConnection(after: String, first: Int!, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! + name: String! + } + + input PersonActedInAggregateInput { + AND: [PersonActedInAggregateInput!] + NOT: PersonActedInAggregateInput + OR: [PersonActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: PersonActedInEdgeAggregationWhereInput + node: PersonActedInNodeAggregationWhereInput + } + + type PersonActedInConnection { + edges: [PersonActedInRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input PersonActedInConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonActedInConnections match this filter + \\"\\"\\" + all: PersonActedInConnectionWhere + \\"\\"\\" + Return People where none of the related PersonActedInConnections match this filter + \\"\\"\\" + none: PersonActedInConnectionWhere + \\"\\"\\" + Return People where one of the related PersonActedInConnections match this filter + \\"\\"\\" + single: PersonActedInConnectionWhere + \\"\\"\\" + Return People where some of the related PersonActedInConnections match this filter + \\"\\"\\" + some: PersonActedInConnectionWhere + } + + input PersonActedInConnectionSort { + edge: PersonActedInEdgeSort + node: ProductionSort + } + + input PersonActedInConnectionWhere { + AND: [PersonActedInConnectionWhere!] + NOT: PersonActedInConnectionWhere + OR: [PersonActedInConnectionWhere!] + edge: PersonActedInEdgeWhere + node: ProductionWhere + } + + input PersonActedInEdgeAggregationWhereInput { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInAggregationWhereInput + } + + input PersonActedInEdgeSort { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInSort + } + + input PersonActedInEdgeWhere { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInWhere + } + + input PersonActedInNodeAggregationWhereInput { + AND: [PersonActedInNodeAggregationWhereInput!] + NOT: PersonActedInNodeAggregationWhereInput + OR: [PersonActedInNodeAggregationWhereInput!] + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type PersonActedInRelationship { + cursor: String! + node: Production! + properties: PersonActedInRelationshipProperties! + } + + union PersonActedInRelationshipProperties = ActedIn + + type PersonAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + type PersonEdge { + cursor: String! + node: Person! + } + + enum PersonImplementation { + Actor + } + + input PersonMoviesAggregateInput { + AND: [PersonMoviesAggregateInput!] + NOT: PersonMoviesAggregateInput + OR: [PersonMoviesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: PersonMoviesEdgeAggregationWhereInput + node: PersonMoviesNodeAggregationWhereInput + } + + type PersonMoviesConnection { + edges: [PersonMoviesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + all: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + none: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + single: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + some: PersonMoviesConnectionWhere + } + + input PersonMoviesConnectionSort { + edge: PersonMoviesEdgeSort + node: MovieSort + } + + input PersonMoviesConnectionWhere { + AND: [PersonMoviesConnectionWhere!] + NOT: PersonMoviesConnectionWhere + OR: [PersonMoviesConnectionWhere!] + edge: PersonMoviesEdgeWhere + node: MovieWhere + } + + input PersonMoviesDeleteFieldInput { + delete: MovieDeleteInput + where: PersonMoviesConnectionWhere + } + + input PersonMoviesDisconnectFieldInput { + disconnect: MovieDisconnectInput + where: PersonMoviesConnectionWhere + } + + input PersonMoviesEdgeAggregationWhereInput { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInAggregationWhereInput + } + + input PersonMoviesEdgeSort { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInSort + } + + input PersonMoviesEdgeWhere { + \\"\\"\\" + Relationship properties when source node is of type: + * Actor + \\"\\"\\" + ActedIn: ActedInWhere + } + + input PersonMoviesNodeAggregationWhereInput { + AND: [PersonMoviesNodeAggregationWhereInput!] + NOT: PersonMoviesNodeAggregationWhereInput + OR: [PersonMoviesNodeAggregationWhereInput!] + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type PersonMoviesRelationship { + cursor: String! + node: Movie! + properties: PersonMoviesRelationshipProperties! + } + + union PersonMoviesRelationshipProperties = ActedIn + + \\"\\"\\" + Fields to sort People by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonSort object. + \\"\\"\\" + input PersonSort { + id: SortDirection + name: SortDirection + } + + input PersonWhere { + AND: [PersonWhere!] + NOT: PersonWhere + OR: [PersonWhere!] + actedIn: ProductionRelationshipFilters + actedInAggregate: PersonActedInAggregateInput + actedInConnection: PersonActedInConnectionFilters + \\"\\"\\" + Return People where all of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_ALL: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where none of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_NONE: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where one of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_SINGLE: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where some of the related PersonActedInConnections match this filter + \\"\\"\\" + actedInConnection_SOME: PersonActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return People where all of the related Productions match this filter\\"\\"\\" + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") + \\"\\"\\"Return People where none of the related Productions match this filter\\"\\"\\" + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") + \\"\\"\\"Return People where one of the related Productions match this filter\\"\\"\\" + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") + \\"\\"\\"Return People where some of the related Productions match this filter\\"\\"\\" + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters + moviesAggregate: PersonMoviesAggregateInput + moviesConnection: PersonMoviesConnectionFilters + \\"\\"\\" + Return People where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return People where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return People where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return People where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return People where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return People where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + typename: [PersonImplementation!] + } + + interface Production { + id: ID! + title: String! + } + + type ProductionAggregateSelection { + count: Int! + title: StringAggregateSelection! + } + + input ProductionConnectWhere { + node: ProductionWhere! + } + + input ProductionCreateInput { + Movie: MovieCreateInput + Series: SeriesCreateInput + } + + type ProductionEdge { + cursor: String! + node: Production! + } + + enum ProductionImplementation { + Movie + Series + } + + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + + \\"\\"\\" + Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. + \\"\\"\\" + input ProductionSort { + id: SortDirection + title: SortDirection + } + + input ProductionUpdateInput { + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input ProductionWhere { + AND: [ProductionWhere!] + NOT: ProductionWhere + OR: [ProductionWhere!] + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + typename: [ProductionImplementation!] + } + + type ProductionsConnection { + edges: [ProductionEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int!, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesConnection(after: String, first: Int!, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + people(limit: Int!, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + peopleAggregate(where: PersonWhere): PersonAggregateSelection! + peopleConnection(after: String, first: Int!, sort: [PersonSort!], where: PersonWhere): PeopleConnection! + productions(limit: Int!, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! + productionsConnection(after: String, first: Int!, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! + series(limit: Int!, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! + seriesConnection(after: String, first: Int!, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! + } + + type Series implements Production { + episodes: Int! + id: ID! + title: String! + } + + type SeriesAggregateSelection { + count: Int! + episodes: IntAggregateSelection! + title: StringAggregateSelection! + } + + type SeriesConnection { + edges: [SeriesEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input SeriesCreateInput { + episodes: Int! + id: ID! + title: String! + } + + type SeriesEdge { + cursor: String! + node: Series! + } + + \\"\\"\\" + Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. + \\"\\"\\" + input SeriesSort { + episodes: SortDirection + id: SortDirection + title: SortDirection + } + + input SeriesUpdateInput { + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input SeriesWhere { + AND: [SeriesWhere!] + NOT: SeriesWhere + OR: [SeriesWhere!] + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + } + + type UpdateSeriesMutationResponse { + info: UpdateInfo! + series: [Series!]! + }" + `); + }); + + test("unions", async () => { + const typeDefs = gql` + interface Production { + id: ID! + title: String! + } + type Movie implements Production @node { + id: ID! + title: String! + runtime: Int! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + type Series implements Production @node { + id: ID! + title: String! + episodes: Int! + } + union Contact = Telephone | Email + type Telephone @node { + number: String! + } + type Email @node { + address: String! + } + type Actor @node { + id: ID! + name: String! + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + contact: [Contact!]! @relationship(type: "HAS_CONTACT", direction: OUT) + } + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs, features: { limitRequired: true } }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + The edge properties for the following fields: + * Movie.actors + * Actor.movies + * Actor.actedIn + \\"\\"\\" + type ActedIn { + screenTime: Int! + } + + input ActedInAggregationWhereInput { + AND: [ActedInAggregationWhereInput!] + NOT: ActedInAggregationWhereInput + OR: [ActedInAggregationWhereInput!] + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") + } + + input ActedInCreateInput { + screenTime: Int! + } + + input ActedInSort { + screenTime: SortDirection + } + + input ActedInUpdateInput { + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + } + + input ActedInWhere { + AND: [ActedInWhere!] + NOT: ActedInWhere + OR: [ActedInWhere!] + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + } + + type Actor { + actedIn(limit: Int!, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + actedInAggregate(where: ProductionWhere): ActorProductionActedInAggregationSelection + actedInConnection(after: String, first: Int!, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + contact(limit: Int!, offset: Int, where: ContactWhere): [Contact!]! + contactConnection(after: String, first: Int!, where: ActorContactConnectionWhere): ActorContactConnection! + id: ID! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int!, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + name: String! + } + + input ActorActedInAggregateInput { + AND: [ActorActedInAggregateInput!] + NOT: ActorActedInAggregateInput + OR: [ActorActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorActedInNodeAggregationWhereInput + } + + input ActorActedInConnectFieldInput { + edge: ActedInCreateInput! + where: ProductionConnectWhere + } + + type ActorActedInConnection { + edges: [ActorActedInRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + + input ActorActedInConnectionSort { + edge: ActedInSort + node: ProductionSort + } + + input ActorActedInConnectionWhere { + AND: [ActorActedInConnectionWhere!] + NOT: ActorActedInConnectionWhere + OR: [ActorActedInConnectionWhere!] + edge: ActedInWhere + node: ProductionWhere + } + + input ActorActedInCreateFieldInput { + edge: ActedInCreateInput! + node: ProductionCreateInput! + } + + input ActorActedInDeleteFieldInput { + where: ActorActedInConnectionWhere + } + + input ActorActedInDisconnectFieldInput { + where: ActorActedInConnectionWhere + } + + input ActorActedInFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + } + + input ActorActedInNodeAggregationWhereInput { + AND: [ActorActedInNodeAggregationWhereInput!] + NOT: ActorActedInNodeAggregationWhereInput + OR: [ActorActedInNodeAggregationWhereInput!] + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type ActorActedInRelationship { + cursor: String! + node: Production! + properties: ActedIn! + } + + input ActorActedInUpdateConnectionInput { + edge: ActedInUpdateInput + node: ProductionUpdateInput + } + + input ActorActedInUpdateFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + delete: [ActorActedInDeleteFieldInput!] + disconnect: [ActorActedInDisconnectFieldInput!] + update: ActorActedInUpdateConnectionInput + where: ActorActedInConnectionWhere + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorConnectInput { + actedIn: [ActorActedInConnectFieldInput!] + contact: ActorContactConnectInput + movies: [ActorMoviesConnectFieldInput!] + } + + input ActorConnectWhere { + node: ActorWhere! + } + + input ActorContactConnectInput { + Email: [ActorContactEmailConnectFieldInput!] + Telephone: [ActorContactTelephoneConnectFieldInput!] + } + + type ActorContactConnection { + edges: [ActorContactRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorContactConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorContactConnections match this filter + \\"\\"\\" + all: ActorContactConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorContactConnections match this filter + \\"\\"\\" + none: ActorContactConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorContactConnections match this filter + \\"\\"\\" + single: ActorContactConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorContactConnections match this filter + \\"\\"\\" + some: ActorContactConnectionWhere + } + + input ActorContactConnectionWhere { + Email: ActorContactEmailConnectionWhere + Telephone: ActorContactTelephoneConnectionWhere + } + + input ActorContactCreateInput { + Email: ActorContactEmailFieldInput + Telephone: ActorContactTelephoneFieldInput + } + + input ActorContactDeleteInput { + Email: [ActorContactEmailDeleteFieldInput!] + Telephone: [ActorContactTelephoneDeleteFieldInput!] + } + + input ActorContactDisconnectInput { + Email: [ActorContactEmailDisconnectFieldInput!] + Telephone: [ActorContactTelephoneDisconnectFieldInput!] + } + + input ActorContactEmailConnectFieldInput { + where: EmailConnectWhere + } + + input ActorContactEmailConnectionWhere { + AND: [ActorContactEmailConnectionWhere!] + NOT: ActorContactEmailConnectionWhere + OR: [ActorContactEmailConnectionWhere!] + node: EmailWhere + } + + input ActorContactEmailCreateFieldInput { + node: EmailCreateInput! + } + + input ActorContactEmailDeleteFieldInput { + where: ActorContactEmailConnectionWhere + } + + input ActorContactEmailDisconnectFieldInput { + where: ActorContactEmailConnectionWhere + } + + input ActorContactEmailFieldInput { + connect: [ActorContactEmailConnectFieldInput!] + create: [ActorContactEmailCreateFieldInput!] + } + + input ActorContactEmailUpdateConnectionInput { + node: EmailUpdateInput + } + + input ActorContactEmailUpdateFieldInput { + connect: [ActorContactEmailConnectFieldInput!] + create: [ActorContactEmailCreateFieldInput!] + delete: [ActorContactEmailDeleteFieldInput!] + disconnect: [ActorContactEmailDisconnectFieldInput!] + update: ActorContactEmailUpdateConnectionInput + where: ActorContactEmailConnectionWhere + } + + type ActorContactRelationship { + cursor: String! + node: Contact! + } + + input ActorContactTelephoneConnectFieldInput { + where: TelephoneConnectWhere + } + + input ActorContactTelephoneConnectionWhere { + AND: [ActorContactTelephoneConnectionWhere!] + NOT: ActorContactTelephoneConnectionWhere + OR: [ActorContactTelephoneConnectionWhere!] + node: TelephoneWhere + } + + input ActorContactTelephoneCreateFieldInput { + node: TelephoneCreateInput! + } + + input ActorContactTelephoneDeleteFieldInput { + where: ActorContactTelephoneConnectionWhere + } + + input ActorContactTelephoneDisconnectFieldInput { + where: ActorContactTelephoneConnectionWhere + } + + input ActorContactTelephoneFieldInput { + connect: [ActorContactTelephoneConnectFieldInput!] + create: [ActorContactTelephoneCreateFieldInput!] + } + + input ActorContactTelephoneUpdateConnectionInput { + node: TelephoneUpdateInput + } + + input ActorContactTelephoneUpdateFieldInput { + connect: [ActorContactTelephoneConnectFieldInput!] + create: [ActorContactTelephoneCreateFieldInput!] + delete: [ActorContactTelephoneDeleteFieldInput!] + disconnect: [ActorContactTelephoneDisconnectFieldInput!] + update: ActorContactTelephoneUpdateConnectionInput + where: ActorContactTelephoneConnectionWhere + } + + input ActorContactUpdateInput { + Email: [ActorContactEmailUpdateFieldInput!] + Telephone: [ActorContactTelephoneUpdateFieldInput!] + } + + input ActorCreateInput { + actedIn: ActorActedInFieldInput + contact: ActorContactCreateInput + id: ID! + movies: ActorMoviesFieldInput + name: String! + } + + input ActorDeleteInput { + actedIn: [ActorActedInDeleteFieldInput!] + contact: ActorContactDeleteInput + movies: [ActorMoviesDeleteFieldInput!] + } + + input ActorDisconnectInput { + actedIn: [ActorActedInDisconnectFieldInput!] + contact: ActorContactDisconnectInput + movies: [ActorMoviesDisconnectFieldInput!] + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + type ActorMovieMoviesAggregationSelection { + count: Int! + edge: ActorMovieMoviesEdgeAggregateSelection + node: ActorMovieMoviesNodeAggregateSelection + } + + type ActorMovieMoviesEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorMovieMoviesNodeAggregateSelection { + runtime: IntAggregateSelection! + title: StringAggregateSelection! + } + + input ActorMoviesAggregateInput { + AND: [ActorMoviesAggregateInput!] + NOT: ActorMoviesAggregateInput + OR: [ActorMoviesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorMoviesNodeAggregationWhereInput + } + + input ActorMoviesConnectFieldInput { + connect: [MovieConnectInput!] + edge: ActedInCreateInput! + where: MovieConnectWhere + } + + type ActorMoviesConnection { + edges: [ActorMoviesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + + input ActorMoviesConnectionSort { + edge: ActedInSort + node: MovieSort + } + + input ActorMoviesConnectionWhere { + AND: [ActorMoviesConnectionWhere!] + NOT: ActorMoviesConnectionWhere + OR: [ActorMoviesConnectionWhere!] + edge: ActedInWhere + node: MovieWhere + } + + input ActorMoviesCreateFieldInput { + edge: ActedInCreateInput! + node: MovieCreateInput! + } + + input ActorMoviesDeleteFieldInput { + delete: MovieDeleteInput + where: ActorMoviesConnectionWhere + } + + input ActorMoviesDisconnectFieldInput { + disconnect: MovieDisconnectInput + where: ActorMoviesConnectionWhere + } + + input ActorMoviesFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + } + + input ActorMoviesNodeAggregationWhereInput { + AND: [ActorMoviesNodeAggregationWhereInput!] + NOT: ActorMoviesNodeAggregationWhereInput + OR: [ActorMoviesNodeAggregationWhereInput!] + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type ActorMoviesRelationship { + cursor: String! + node: Movie! + properties: ActedIn! + } + + input ActorMoviesUpdateConnectionInput { + edge: ActedInUpdateInput + node: MovieUpdateInput + } + + input ActorMoviesUpdateFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + delete: [ActorMoviesDeleteFieldInput!] + disconnect: [ActorMoviesDisconnectFieldInput!] + update: ActorMoviesUpdateConnectionInput + where: ActorMoviesConnectionWhere + } + + type ActorProductionActedInAggregationSelection { + count: Int! + edge: ActorProductionActedInEdgeAggregateSelection + node: ActorProductionActedInNodeAggregateSelection + } + + type ActorProductionActedInEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorProductionActedInNodeAggregateSelection { + title: StringAggregateSelection! + } + + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + id: SortDirection + name: SortDirection + } + + input ActorUpdateInput { + actedIn: [ActorActedInUpdateFieldInput!] + contact: ActorContactUpdateInput + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [ActorMoviesUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + actedIn: ProductionRelationshipFilters + actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" + actedIn_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" + actedIn_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" + actedIn_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" + actedIn_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + contact: ContactRelationshipFilters + contactConnection: ActorContactConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorContactConnections match this filter + \\"\\"\\" + contactConnection_ALL: ActorContactConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contactConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorContactConnections match this filter + \\"\\"\\" + contactConnection_NONE: ActorContactConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contactConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorContactConnections match this filter + \\"\\"\\" + contactConnection_SINGLE: ActorContactConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contactConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorContactConnections match this filter + \\"\\"\\" + contactConnection_SOME: ActorContactConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'contactConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Contacts match this filter\\"\\"\\" + contact_ALL: ContactWhere @deprecated(reason: \\"Please use the relevant generic filter 'contact: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Contacts match this filter\\"\\"\\" + contact_NONE: ContactWhere @deprecated(reason: \\"Please use the relevant generic filter 'contact: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Contacts match this filter\\"\\"\\" + contact_SINGLE: ContactWhere @deprecated(reason: \\"Please use the relevant generic filter 'contact: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Contacts match this filter\\"\\"\\" + contact_SOME: ContactWhere @deprecated(reason: \\"Please use the relevant generic filter 'contact: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters + moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + union Contact = Email | Telephone + + input ContactRelationshipFilters { + \\"\\"\\"Filter type where all of the related Contacts match this filter\\"\\"\\" + all: ContactWhere + \\"\\"\\"Filter type where none of the related Contacts match this filter\\"\\"\\" + none: ContactWhere + \\"\\"\\"Filter type where one of the related Contacts match this filter\\"\\"\\" + single: ContactWhere + \\"\\"\\"Filter type where some of the related Contacts match this filter\\"\\"\\" + some: ContactWhere + } + + input ContactWhere { + Email: EmailWhere + Telephone: TelephoneWhere + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + type CreateEmailsMutationResponse { + emails: [Email!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + type CreateSeriesMutationResponse { + info: CreateInfo! + series: [Series!]! + } + + type CreateTelephonesMutationResponse { + info: CreateInfo! + telephones: [Telephone!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + type Email { + address: String! + } + + type EmailAggregateSelection { + address: StringAggregateSelection! + count: Int! + } + + input EmailConnectWhere { + node: EmailWhere! + } + + input EmailCreateInput { + address: String! + } + + type EmailEdge { + cursor: String! + node: Email! + } + + \\"\\"\\" + Fields to sort Emails by. The order in which sorts are applied is not guaranteed when specifying many fields in one EmailSort object. + \\"\\"\\" + input EmailSort { + address: SortDirection + } + + input EmailUpdateInput { + address: StringScalarMutations + address_SET: String @deprecated(reason: \\"Please use the generic mutation 'address: { set: ... } }' instead.\\") + } + + input EmailWhere { + AND: [EmailWhere!] + NOT: EmailWhere + OR: [EmailWhere!] + address: StringScalarFilters + address_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter address: { contains: ... }\\") + address_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter address: { endsWith: ... }\\") + address_EQ: String @deprecated(reason: \\"Please use the relevant generic filter address: { eq: ... }\\") + address_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter address: { in: ... }\\") + address_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter address: { startsWith: ... }\\") + } + + type EmailsConnection { + edges: [EmailEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + type Movie implements Production { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int!, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + id: ID! + runtime: Int! + title: String! + } + + type MovieActorActorsAggregationSelection { + count: Int! + edge: MovieActorActorsEdgeAggregateSelection + node: MovieActorActorsNodeAggregateSelection + } + + type MovieActorActorsEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type MovieActorActorsNodeAggregateSelection { + name: StringAggregateSelection! + } + + input MovieActorsAggregateInput { + AND: [MovieActorsAggregateInput!] + NOT: MovieActorsAggregateInput + OR: [MovieActorsAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: MovieActorsNodeAggregationWhereInput + } + + input MovieActorsConnectFieldInput { + connect: [ActorConnectInput!] + edge: ActedInCreateInput! + where: ActorConnectWhere + } + + type MovieActorsConnection { + edges: [MovieActorsRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + + input MovieActorsConnectionSort { + edge: ActedInSort + node: ActorSort + } + + input MovieActorsConnectionWhere { + AND: [MovieActorsConnectionWhere!] + NOT: MovieActorsConnectionWhere + OR: [MovieActorsConnectionWhere!] + edge: ActedInWhere + node: ActorWhere + } + + input MovieActorsCreateFieldInput { + edge: ActedInCreateInput! + node: ActorCreateInput! + } + + input MovieActorsDeleteFieldInput { + delete: ActorDeleteInput + where: MovieActorsConnectionWhere + } + + input MovieActorsDisconnectFieldInput { + disconnect: ActorDisconnectInput + where: MovieActorsConnectionWhere + } + + input MovieActorsFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + } + + input MovieActorsNodeAggregationWhereInput { + AND: [MovieActorsNodeAggregationWhereInput!] + NOT: MovieActorsNodeAggregationWhereInput + OR: [MovieActorsNodeAggregationWhereInput!] + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + } + + type MovieActorsRelationship { + cursor: String! + node: Actor! + properties: ActedIn! + } + + input MovieActorsUpdateConnectionInput { + edge: ActedInUpdateInput + node: ActorUpdateInput + } + + input MovieActorsUpdateFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + delete: [MovieActorsDeleteFieldInput!] + disconnect: [MovieActorsDisconnectFieldInput!] + update: MovieActorsUpdateConnectionInput + where: MovieActorsConnectionWhere + } + + type MovieAggregateSelection { + count: Int! + runtime: IntAggregateSelection! + title: StringAggregateSelection! + } + + input MovieConnectInput { + actors: [MovieActorsConnectFieldInput!] + } + + input MovieConnectWhere { + node: MovieWhere! + } + + input MovieCreateInput { + actors: MovieActorsFieldInput + id: ID! + runtime: Int! + title: String! + } + + input MovieDeleteInput { + actors: [MovieActorsDeleteFieldInput!] + } + + input MovieDisconnectInput { + actors: [MovieActorsDisconnectFieldInput!] + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + id: SortDirection + runtime: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + actors: [MovieActorsUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + actors: ActorRelationshipFilters + actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createEmails(input: [EmailCreateInput!]!): CreateEmailsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! + createTelephones(input: [TelephoneCreateInput!]!): CreateTelephonesMutationResponse! + deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! + deleteEmails(where: EmailWhere): DeleteInfo! + deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! + deleteSeries(where: SeriesWhere): DeleteInfo! + deleteTelephones(where: TelephoneWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateEmails(update: EmailUpdateInput, where: EmailWhere): UpdateEmailsMutationResponse! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! + updateTelephones(update: TelephoneUpdateInput, where: TelephoneWhere): UpdateTelephonesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + interface Production { + id: ID! + title: String! + } + + type ProductionAggregateSelection { + count: Int! + title: StringAggregateSelection! + } + + input ProductionConnectWhere { + node: ProductionWhere! + } + + input ProductionCreateInput { + Movie: MovieCreateInput + Series: SeriesCreateInput + } + + type ProductionEdge { + cursor: String! + node: Production! + } + + enum ProductionImplementation { + Movie + Series + } + + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere + } + + \\"\\"\\" + Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. + \\"\\"\\" + input ProductionSort { + id: SortDirection + title: SortDirection + } + + input ProductionUpdateInput { + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input ProductionWhere { + AND: [ProductionWhere!] + NOT: ProductionWhere + OR: [ProductionWhere!] + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + typename: [ProductionImplementation!] + } + + type ProductionsConnection { + edges: [ProductionEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int!, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + contacts(limit: Int!, offset: Int, where: ContactWhere): [Contact!]! + emails(limit: Int!, offset: Int, sort: [EmailSort!], where: EmailWhere): [Email!]! + emailsAggregate(where: EmailWhere): EmailAggregateSelection! + emailsConnection(after: String, first: Int!, sort: [EmailSort!], where: EmailWhere): EmailsConnection! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesConnection(after: String, first: Int!, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + productions(limit: Int!, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! + productionsConnection(after: String, first: Int!, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! + series(limit: Int!, offset: Int, sort: [SeriesSort!], where: SeriesWhere): [Series!]! + seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! + seriesConnection(after: String, first: Int!, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! + telephones(limit: Int!, offset: Int, sort: [TelephoneSort!], where: TelephoneWhere): [Telephone!]! + telephonesAggregate(where: TelephoneWhere): TelephoneAggregateSelection! + telephonesConnection(after: String, first: Int!, sort: [TelephoneSort!], where: TelephoneWhere): TelephonesConnection! + } + + type Series implements Production { + episodes: Int! + id: ID! + title: String! + } + + type SeriesAggregateSelection { + count: Int! + episodes: IntAggregateSelection! + title: StringAggregateSelection! + } + + type SeriesConnection { + edges: [SeriesEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input SeriesCreateInput { + episodes: Int! + id: ID! + title: String! + } + + type SeriesEdge { + cursor: String! + node: Series! + } + + \\"\\"\\" + Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. + \\"\\"\\" + input SeriesSort { + episodes: SortDirection + id: SortDirection + title: SortDirection + } + + input SeriesUpdateInput { + episodes: IntScalarMutations + episodes_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { decrement: ... } }' instead.\\") + episodes_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'episodes: { increment: ... } }' instead.\\") + episodes_SET: Int @deprecated(reason: \\"Please use the generic mutation 'episodes: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input SeriesWhere { + AND: [SeriesWhere!] + NOT: SeriesWhere + OR: [SeriesWhere!] + episodes: IntScalarFilters + episodes_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { eq: ... }\\") + episodes_GT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gt: ... }\\") + episodes_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { gte: ... }\\") + episodes_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter episodes: { in: ... }\\") + episodes_LT: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lt: ... }\\") + episodes_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter episodes: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type Telephone { + number: String! + } + + type TelephoneAggregateSelection { + count: Int! + number: StringAggregateSelection! + } + + input TelephoneConnectWhere { + node: TelephoneWhere! + } + + input TelephoneCreateInput { + number: String! + } + + type TelephoneEdge { + cursor: String! + node: Telephone! + } + + \\"\\"\\" + Fields to sort Telephones by. The order in which sorts are applied is not guaranteed when specifying many fields in one TelephoneSort object. + \\"\\"\\" + input TelephoneSort { + number: SortDirection + } + + input TelephoneUpdateInput { + number: StringScalarMutations + number_SET: String @deprecated(reason: \\"Please use the generic mutation 'number: { set: ... } }' instead.\\") + } + + input TelephoneWhere { + AND: [TelephoneWhere!] + NOT: TelephoneWhere + OR: [TelephoneWhere!] + number: StringScalarFilters + number_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter number: { contains: ... }\\") + number_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter number: { endsWith: ... }\\") + number_EQ: String @deprecated(reason: \\"Please use the relevant generic filter number: { eq: ... }\\") + number_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter number: { in: ... }\\") + number_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter number: { startsWith: ... }\\") + } + + type TelephonesConnection { + edges: [TelephoneEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + type UpdateEmailsMutationResponse { + emails: [Email!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + } + + type UpdateSeriesMutationResponse { + info: UpdateInfo! + series: [Series!]! + } + + type UpdateTelephonesMutationResponse { + info: UpdateInfo! + telephones: [Telephone!]! + }" + `); + }); + + test("@fulltext", async () => { + const typeDefs = gql` + type Movie + @node + @fulltext( + indexes: [ + { indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] } + { indexName: "MovieDescription", queryName: "moviesByDescription", fields: ["description"] } + ] + ) { + title: String + description: String + } + type Actor @node { + id: ID! + name: String! + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs, features: { limitRequired: true } }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + The edge properties for the following fields: + * Actor.movies + \\"\\"\\" + type ActedIn { + screenTime: Int! + } + + input ActedInAggregationWhereInput { + AND: [ActedInAggregationWhereInput!] + NOT: ActedInAggregationWhereInput + OR: [ActedInAggregationWhereInput!] + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") + } + + input ActedInCreateInput { + screenTime: Int! + } + + input ActedInSort { + screenTime: SortDirection + } + + input ActedInUpdateInput { + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + } + + input ActedInWhere { + AND: [ActedInWhere!] + NOT: ActedInWhere + OR: [ActedInWhere!] + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + } + + type Actor { + id: ID! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int!, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + name: String! + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorCreateInput { + id: ID! + movies: ActorMoviesFieldInput + name: String! + } + + input ActorDeleteInput { + movies: [ActorMoviesDeleteFieldInput!] + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + type ActorMovieMoviesAggregationSelection { + count: Int! + edge: ActorMovieMoviesEdgeAggregateSelection + node: ActorMovieMoviesNodeAggregateSelection + } + + type ActorMovieMoviesEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorMovieMoviesNodeAggregateSelection { + description: StringAggregateSelection! + title: StringAggregateSelection! + } + + input ActorMoviesAggregateInput { + AND: [ActorMoviesAggregateInput!] + NOT: ActorMoviesAggregateInput + OR: [ActorMoviesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorMoviesNodeAggregationWhereInput + } + + input ActorMoviesConnectFieldInput { + edge: ActedInCreateInput! + where: MovieConnectWhere + } + + type ActorMoviesConnection { + edges: [ActorMoviesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + + input ActorMoviesConnectionSort { + edge: ActedInSort + node: MovieSort + } + + input ActorMoviesConnectionWhere { + AND: [ActorMoviesConnectionWhere!] + NOT: ActorMoviesConnectionWhere + OR: [ActorMoviesConnectionWhere!] + edge: ActedInWhere + node: MovieWhere + } + + input ActorMoviesCreateFieldInput { + edge: ActedInCreateInput! + node: MovieCreateInput! + } + + input ActorMoviesDeleteFieldInput { + where: ActorMoviesConnectionWhere + } + + input ActorMoviesDisconnectFieldInput { + where: ActorMoviesConnectionWhere + } + + input ActorMoviesFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + } + + input ActorMoviesNodeAggregationWhereInput { + AND: [ActorMoviesNodeAggregationWhereInput!] + NOT: ActorMoviesNodeAggregationWhereInput + OR: [ActorMoviesNodeAggregationWhereInput!] + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type ActorMoviesRelationship { + cursor: String! + node: Movie! + properties: ActedIn! + } + + input ActorMoviesUpdateConnectionInput { + edge: ActedInUpdateInput + node: MovieUpdateInput + } + + input ActorMoviesUpdateFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + delete: [ActorMoviesDeleteFieldInput!] + disconnect: [ActorMoviesDisconnectFieldInput!] + update: ActorMoviesUpdateConnectionInput + where: ActorMoviesConnectionWhere + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + id: SortDirection + name: SortDirection + } + + input ActorUpdateInput { + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [ActorMoviesUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters + moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"The input for filtering a float\\"\\"\\" + input FloatWhere { + max: Float + min: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + type Movie { + description: String + title: String + } + + type MovieAggregateSelection { + count: Int! + description: StringAggregateSelection! + title: StringAggregateSelection! + } + + input MovieConnectWhere { + node: MovieWhere! + } + + input MovieCreateInput { + description: String + title: String + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + type MovieIndexEdge { + cursor: String! + node: Movie! + score: Float! + } + + \\"\\"\\"The input for sorting a Fulltext query on an index of Movie\\"\\"\\" + input MovieIndexSort { + node: MovieSort + score: SortDirection + } + + \\"\\"\\"The input for filtering a full-text query on an index of Movie\\"\\"\\" + input MovieIndexWhere { + node: MovieWhere + score: FloatWhere + } + + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + description: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type MoviesIndexConnection { + edges: [MovieIndexEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! + deleteMovies(where: MovieWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Query { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int!, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesByDescription(after: String, first: Int!, phrase: String!, sort: [MovieIndexSort!], where: MovieIndexWhere): MoviesIndexConnection! + moviesByTitle(after: String, first: Int!, phrase: String!, sort: [MovieIndexSort!], where: MovieIndexWhere): MoviesIndexConnection! + moviesConnection(after: String, first: Int!, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + }" + `); + }); + + test("@vector", async () => { + const typeDefs = gql` + type Movie + @node + @vector( + indexes: [ + { indexName: "MovieTitle", embeddingProperty: "title", queryName: "titleQuery" } + { + indexName: "MovieDescription" + embeddingProperty: "description" + queryName: "descriptionQuery" + } + ] + ) { + title: String + description: String + } + type Actor @node { + id: ID! + name: String! + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + const neoSchema = new Neo4jGraphQL({ typeDefs, features: { limitRequired: true } }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + The edge properties for the following fields: + * Actor.movies + \\"\\"\\" + type ActedIn { + screenTime: Int! + } + + input ActedInAggregationWhereInput { + AND: [ActedInAggregationWhereInput!] + NOT: ActedInAggregationWhereInput + OR: [ActedInAggregationWhereInput!] + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") + } + + input ActedInCreateInput { + screenTime: Int! + } + + input ActedInSort { + screenTime: SortDirection + } + + input ActedInUpdateInput { + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + } + + input ActedInWhere { + AND: [ActedInWhere!] + NOT: ActedInWhere + OR: [ActedInWhere!] + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + } + + type Actor { + id: ID! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int!, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + name: String! + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorCreateInput { + id: ID! + movies: ActorMoviesFieldInput + name: String! + } + + input ActorDeleteInput { + movies: [ActorMoviesDeleteFieldInput!] + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + type ActorMovieMoviesAggregationSelection { + count: Int! + edge: ActorMovieMoviesEdgeAggregateSelection + node: ActorMovieMoviesNodeAggregateSelection + } + + type ActorMovieMoviesEdgeAggregateSelection { + screenTime: IntAggregateSelection! + } + + type ActorMovieMoviesNodeAggregateSelection { + description: StringAggregateSelection! + title: StringAggregateSelection! + } + + input ActorMoviesAggregateInput { + AND: [ActorMoviesAggregateInput!] + NOT: ActorMoviesAggregateInput + OR: [ActorMoviesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorMoviesNodeAggregationWhereInput + } + + input ActorMoviesConnectFieldInput { + edge: ActedInCreateInput! + where: MovieConnectWhere + } + + type ActorMoviesConnection { + edges: [ActorMoviesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + + input ActorMoviesConnectionSort { + edge: ActedInSort + node: MovieSort + } + + input ActorMoviesConnectionWhere { + AND: [ActorMoviesConnectionWhere!] + NOT: ActorMoviesConnectionWhere + OR: [ActorMoviesConnectionWhere!] + edge: ActedInWhere + node: MovieWhere + } + + input ActorMoviesCreateFieldInput { + edge: ActedInCreateInput! + node: MovieCreateInput! + } + + input ActorMoviesDeleteFieldInput { + where: ActorMoviesConnectionWhere + } + + input ActorMoviesDisconnectFieldInput { + where: ActorMoviesConnectionWhere + } + + input ActorMoviesFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + } + + input ActorMoviesNodeAggregationWhereInput { + AND: [ActorMoviesNodeAggregationWhereInput!] + NOT: ActorMoviesNodeAggregationWhereInput + OR: [ActorMoviesNodeAggregationWhereInput!] + description: StringScalarAggregationFilters + description_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { eq: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { gte: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lt: ... } } }' instead.\\") + description_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'description: { averageLength: { lte: ... } } }' instead.\\") + description_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { eq: ... } } }' instead.\\") + description_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gt: ... } } }' instead.\\") + description_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { gte: ... } } }' instead.\\") + description_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lt: ... } } }' instead.\\") + description_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { longestLength: { lte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { eq: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { gte: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lt: ... } } }' instead.\\") + description_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'description: { shortestLength: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") + } + + type ActorMoviesRelationship { + cursor: String! + node: Movie! + properties: ActedIn! + } + + input ActorMoviesUpdateConnectionInput { + edge: ActedInUpdateInput + node: MovieUpdateInput + } + + input ActorMoviesUpdateFieldInput { + connect: [ActorMoviesConnectFieldInput!] + create: [ActorMoviesCreateFieldInput!] + delete: [ActorMoviesDeleteFieldInput!] + disconnect: [ActorMoviesDisconnectFieldInput!] + update: ActorMoviesUpdateConnectionInput + where: ActorMoviesConnectionWhere + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + id: SortDirection + name: SortDirection + } + + input ActorUpdateInput { + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + movies: [ActorMoviesUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + movies: MovieRelationshipFilters + moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"The input for filtering a float\\"\\"\\" + input FloatWhere { + max: Float + min: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + type Movie { + description: String + title: String + } + + type MovieAggregateSelection { + count: Int! + description: StringAggregateSelection! + title: StringAggregateSelection! + } + + input MovieConnectWhere { + node: MovieWhere! + } + + input MovieCreateInput { + description: String + title: String + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + type MovieIndexEdge { + cursor: String! + node: Movie! + score: Float! + } + + \\"\\"\\"The input for sorting a Vector query on an index of Movie\\"\\"\\" + input MovieIndexSort { + node: MovieSort + score: SortDirection + } + + \\"\\"\\"The input for filtering a Vector query on an index of Movie\\"\\"\\" + input MovieIndexWhere { + node: MovieWhere + score: FloatWhere + } + + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + description: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type MoviesIndexConnection { + edges: [MovieIndexEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! + deleteMovies(where: MovieWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Query { + actors(limit: Int!, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int!, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + descriptionQuery(after: String, first: Int!, sort: [MovieIndexSort!], vector: [Float!], where: MovieIndexWhere): MoviesIndexConnection! + movies(limit: Int!, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesConnection(after: String, first: Int!, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + titleQuery(after: String, first: Int!, sort: [MovieIndexSort!], vector: [Float!], where: MovieIndexWhere): MoviesIndexConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/lowercase-type-names.test.ts b/packages/graphql/tests/schema/lowercase-type-names.test.ts index ea7956d1fc..aefb674e1a 100644 --- a/packages/graphql/tests/schema/lowercase-type-names.test.ts +++ b/packages/graphql/tests/schema/lowercase-type-names.test.ts @@ -82,6 +82,27 @@ describe("lower case type names", () => { min: DateTime } + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -90,6 +111,16 @@ describe("lower case type names", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type IntAggregateSelection { average: Float max: Int @@ -97,6 +128,31 @@ describe("lower case type names", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type MoviesConnection { edges: [movieEdge!]! pageInfo: PageInfo! @@ -121,10 +177,10 @@ describe("lower case type names", () => { } type Query { - actors(limit: Int, offset: Int, options: actorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [actorSort!], where: actorWhere): [actor!]! + actors(limit: Int, offset: Int, sort: [actorSort!], where: actorWhere): [actor!]! actorsAggregate(where: actorWhere): actorAggregateSelection! actorsConnection(after: String, first: Int, sort: [actorSort!], where: actorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: movieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [movieSort!], where: movieWhere): [movie!]! + movies(limit: Int, offset: Int, sort: [movieSort!], where: movieWhere): [movie!]! moviesAggregate(where: movieWhere): movieAggregateSelection! moviesConnection(after: String, first: Int, sort: [movieSort!], where: movieWhere): MoviesConnection! } @@ -142,6 +198,27 @@ describe("lower case type names", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [actor!]! info: UpdateInfo! @@ -164,9 +241,9 @@ describe("lower case type names", () => { type actor { createdAt: DateTime - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: movieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [movieSort!], where: movieWhere): [movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: movieWhere): actormovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [actorMoviesConnectionSort!], where: actorMoviesConnectionWhere): actorMoviesConnection! + movies(limit: Int, offset: Int, sort: [movieSort!], where: movieWhere): [movie!]! + moviesAggregate(where: movieWhere): actormovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [actorMoviesConnectionSort!], where: actorMoviesConnectionWhere): actorMoviesConnection! name: String year: Int } @@ -210,7 +287,7 @@ describe("lower case type names", () => { AND: [actorMoviesAggregateInput!] NOT: actorMoviesAggregateInput OR: [actorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -221,10 +298,6 @@ describe("lower case type names", () => { input actorMoviesConnectFieldInput { connect: [movieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: movieConnectWhere } @@ -234,6 +307,25 @@ describe("lower case type names", () => { totalCount: Int! } + input actorMoviesConnectionFilters { + \\"\\"\\" + Return actors where all of the related actorMoviesConnections match this filter + \\"\\"\\" + all: actorMoviesConnectionWhere + \\"\\"\\" + Return actors where none of the related actorMoviesConnections match this filter + \\"\\"\\" + none: actorMoviesConnectionWhere + \\"\\"\\" + Return actors where one of the related actorMoviesConnections match this filter + \\"\\"\\" + single: actorMoviesConnectionWhere + \\"\\"\\" + Return actors where some of the related actorMoviesConnections match this filter + \\"\\"\\" + some: actorMoviesConnectionWhere + } + input actorMoviesConnectionSort { node: movieSort } @@ -268,66 +360,70 @@ describe("lower case type names", () => { AND: [actorMoviesNodeAggregationWhereInput!] NOT: actorMoviesNodeAggregationWhereInput OR: [actorMoviesNodeAggregationWhereInput!] - createdAt_MAX_EQUAL: DateTime - createdAt_MAX_GT: DateTime - createdAt_MAX_GTE: DateTime - createdAt_MAX_LT: DateTime - createdAt_MAX_LTE: DateTime - createdAt_MIN_EQUAL: DateTime - createdAt_MIN_GT: DateTime - createdAt_MIN_GTE: DateTime - createdAt_MIN_LT: DateTime - createdAt_MIN_LTE: DateTime - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - testId_AVERAGE_LENGTH_EQUAL: Float - testId_AVERAGE_LENGTH_GT: Float - testId_AVERAGE_LENGTH_GTE: Float - testId_AVERAGE_LENGTH_LT: Float - testId_AVERAGE_LENGTH_LTE: Float - testId_LONGEST_LENGTH_EQUAL: Int - testId_LONGEST_LENGTH_GT: Int - testId_LONGEST_LENGTH_GTE: Int - testId_LONGEST_LENGTH_LT: Int - testId_LONGEST_LENGTH_LTE: Int - testId_SHORTEST_LENGTH_EQUAL: Int - testId_SHORTEST_LENGTH_GT: Int - testId_SHORTEST_LENGTH_GTE: Int - testId_SHORTEST_LENGTH_LT: Int - testId_SHORTEST_LENGTH_LTE: Int - year_AVERAGE_EQUAL: Float - year_AVERAGE_GT: Float - year_AVERAGE_GTE: Float - year_AVERAGE_LT: Float - year_AVERAGE_LTE: Float - year_MAX_EQUAL: Int - year_MAX_GT: Int - year_MAX_GTE: Int - year_MAX_LT: Int - year_MAX_LTE: Int - year_MIN_EQUAL: Int - year_MIN_GT: Int - year_MIN_GTE: Int - year_MIN_LT: Int - year_MIN_LTE: Int - year_SUM_EQUAL: Int - year_SUM_GT: Int - year_SUM_GTE: Int - year_SUM_LT: Int - year_SUM_LTE: Int + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + testId: StringScalarAggregationFilters + testId_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'testId: { averageLength: { eq: ... } } }' instead.\\") + testId_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'testId: { averageLength: { gt: ... } } }' instead.\\") + testId_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'testId: { averageLength: { gte: ... } } }' instead.\\") + testId_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'testId: { averageLength: { lt: ... } } }' instead.\\") + testId_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'testId: { averageLength: { lte: ... } } }' instead.\\") + testId_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { longestLength: { eq: ... } } }' instead.\\") + testId_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { longestLength: { gt: ... } } }' instead.\\") + testId_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { longestLength: { gte: ... } } }' instead.\\") + testId_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { longestLength: { lt: ... } } }' instead.\\") + testId_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { longestLength: { lte: ... } } }' instead.\\") + testId_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { shortestLength: { eq: ... } } }' instead.\\") + testId_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { shortestLength: { gt: ... } } }' instead.\\") + testId_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { shortestLength: { gte: ... } } }' instead.\\") + testId_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { shortestLength: { lt: ... } } }' instead.\\") + testId_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'testId: { shortestLength: { lte: ... } } }' instead.\\") + year: IntScalarAggregationFilters + year_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { eq: ... } } }' instead.\\") + year_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gt: ... } } }' instead.\\") + year_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gte: ... } } }' instead.\\") + year_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lt: ... } } }' instead.\\") + year_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lte: ... } } }' instead.\\") + year_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { eq: ... } } }' instead.\\") + year_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gt: ... } } }' instead.\\") + year_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gte: ... } } }' instead.\\") + year_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lt: ... } } }' instead.\\") + year_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lte: ... } } }' instead.\\") + year_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { eq: ... } } }' instead.\\") + year_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gt: ... } } }' instead.\\") + year_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gte: ... } } }' instead.\\") + year_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lt: ... } } }' instead.\\") + year_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lte: ... } } }' instead.\\") + year_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { eq: ... } } }' instead.\\") + year_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gt: ... } } }' instead.\\") + year_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gte: ... } } }' instead.\\") + year_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lt: ... } } }' instead.\\") + year_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lte: ... } } }' instead.\\") } type actorMoviesRelationship { @@ -348,13 +444,15 @@ describe("lower case type names", () => { where: actorMoviesConnectionWhere } - input actorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more actorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [actorSort!] + input actorRelationshipFilters { + \\"\\"\\"Filter type where all of the related actors match this filter\\"\\"\\" + all: actorWhere + \\"\\"\\"Filter type where none of the related actors match this filter\\"\\"\\" + none: actorWhere + \\"\\"\\"Filter type where one of the related actors match this filter\\"\\"\\" + single: actorWhere + \\"\\"\\"Filter type where some of the related actors match this filter\\"\\"\\" + some: actorWhere } \\"\\"\\" @@ -367,66 +465,68 @@ describe("lower case type names", () => { } input actorUpdateInput { - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") movies: [actorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + year: IntScalarMutations + year_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { decrement: ... } }' instead.\\") + year_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { increment: ... } }' instead.\\") + year_SET: Int @deprecated(reason: \\"Please use the generic mutation 'year: { set: ... } }' instead.\\") } input actorWhere { AND: [actorWhere!] NOT: actorWhere OR: [actorWhere!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime] - createdAt_LT: DateTime - createdAt_LTE: DateTime + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + movies: movieRelationshipFilters moviesAggregate: actorMoviesAggregateInput + moviesConnection: actorMoviesConnectionFilters \\"\\"\\" Return actors where all of the related actorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: actorMoviesConnectionWhere + moviesConnection_ALL: actorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return actors where none of the related actorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: actorMoviesConnectionWhere + moviesConnection_NONE: actorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return actors where one of the related actorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: actorMoviesConnectionWhere + moviesConnection_SINGLE: actorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return actors where some of the related actorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: actorMoviesConnectionWhere + moviesConnection_SOME: actorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return actors where all of the related movies match this filter\\"\\"\\" - movies_ALL: movieWhere + movies_ALL: movieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return actors where none of the related movies match this filter\\"\\"\\" - movies_NONE: movieWhere + movies_NONE: movieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return actors where one of the related movies match this filter\\"\\"\\" - movies_SINGLE: movieWhere + movies_SINGLE: movieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return actors where some of the related movies match this filter\\"\\"\\" - movies_SOME: movieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int] - year_LT: Int - year_LTE: Int + movies_SOME: movieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + year: IntScalarFilters + year_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter year: { eq: ... }\\") + year_GT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gt: ... }\\") + year_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gte: ... }\\") + year_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter year: { in: ... }\\") + year_LT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lt: ... }\\") + year_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lte: ... }\\") } type actormovieMoviesAggregationSelection { @@ -442,9 +542,9 @@ describe("lower case type names", () => { } type movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: actorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [actorSort!], where: actorWhere): [actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: actorWhere): movieactorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [movieActorsConnectionSort!], where: movieActorsConnectionWhere): movieActorsConnection! + actors(limit: Int, offset: Int, sort: [actorSort!], where: actorWhere): [actor!]! + actorsAggregate(where: actorWhere): movieactorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [movieActorsConnectionSort!], where: movieActorsConnectionWhere): movieActorsConnection! createdAt: DateTime name: String testId: String @@ -455,7 +555,7 @@ describe("lower case type names", () => { AND: [movieActorsAggregateInput!] NOT: movieActorsAggregateInput OR: [movieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -466,10 +566,6 @@ describe("lower case type names", () => { input movieActorsConnectFieldInput { connect: [actorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: actorConnectWhere } @@ -479,6 +575,25 @@ describe("lower case type names", () => { totalCount: Int! } + input movieActorsConnectionFilters { + \\"\\"\\" + Return movies where all of the related movieActorsConnections match this filter + \\"\\"\\" + all: movieActorsConnectionWhere + \\"\\"\\" + Return movies where none of the related movieActorsConnections match this filter + \\"\\"\\" + none: movieActorsConnectionWhere + \\"\\"\\" + Return movies where one of the related movieActorsConnections match this filter + \\"\\"\\" + single: movieActorsConnectionWhere + \\"\\"\\" + Return movies where some of the related movieActorsConnections match this filter + \\"\\"\\" + some: movieActorsConnectionWhere + } + input movieActorsConnectionSort { node: actorSort } @@ -513,51 +628,54 @@ describe("lower case type names", () => { AND: [movieActorsNodeAggregationWhereInput!] NOT: movieActorsNodeAggregationWhereInput OR: [movieActorsNodeAggregationWhereInput!] - createdAt_MAX_EQUAL: DateTime - createdAt_MAX_GT: DateTime - createdAt_MAX_GTE: DateTime - createdAt_MAX_LT: DateTime - createdAt_MAX_LTE: DateTime - createdAt_MIN_EQUAL: DateTime - createdAt_MIN_GT: DateTime - createdAt_MIN_GTE: DateTime - createdAt_MIN_LT: DateTime - createdAt_MIN_LTE: DateTime - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - year_AVERAGE_EQUAL: Float - year_AVERAGE_GT: Float - year_AVERAGE_GTE: Float - year_AVERAGE_LT: Float - year_AVERAGE_LTE: Float - year_MAX_EQUAL: Int - year_MAX_GT: Int - year_MAX_GTE: Int - year_MAX_LT: Int - year_MAX_LTE: Int - year_MIN_EQUAL: Int - year_MIN_GT: Int - year_MIN_GTE: Int - year_MIN_LT: Int - year_MIN_LTE: Int - year_SUM_EQUAL: Int - year_SUM_GT: Int - year_SUM_GTE: Int - year_SUM_LT: Int - year_SUM_LTE: Int + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + year: IntScalarAggregationFilters + year_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { eq: ... } } }' instead.\\") + year_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gt: ... } } }' instead.\\") + year_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { gte: ... } } }' instead.\\") + year_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lt: ... } } }' instead.\\") + year_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'year: { average: { lte: ... } } }' instead.\\") + year_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { eq: ... } } }' instead.\\") + year_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gt: ... } } }' instead.\\") + year_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { gte: ... } } }' instead.\\") + year_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lt: ... } } }' instead.\\") + year_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { max: { lte: ... } } }' instead.\\") + year_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { eq: ... } } }' instead.\\") + year_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gt: ... } } }' instead.\\") + year_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { gte: ... } } }' instead.\\") + year_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lt: ... } } }' instead.\\") + year_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { min: { lte: ... } } }' instead.\\") + year_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { eq: ... } } }' instead.\\") + year_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gt: ... } } }' instead.\\") + year_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { gte: ... } } }' instead.\\") + year_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lt: ... } } }' instead.\\") + year_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'year: { sum: { lte: ... } } }' instead.\\") } type movieActorsRelationship { @@ -615,13 +733,15 @@ describe("lower case type names", () => { node: movie! } - input movieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more movieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [movieSort!] + input movieRelationshipFilters { + \\"\\"\\"Filter type where all of the related movies match this filter\\"\\"\\" + all: movieWhere + \\"\\"\\"Filter type where none of the related movies match this filter\\"\\"\\" + none: movieWhere + \\"\\"\\"Filter type where one of the related movies match this filter\\"\\"\\" + single: movieWhere + \\"\\"\\"Filter type where some of the related movies match this filter\\"\\"\\" + some: movieWhere } \\"\\"\\" @@ -636,73 +756,75 @@ describe("lower case type names", () => { input movieUpdateInput { actors: [movieActorsUpdateFieldInput!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - testId: String @deprecated(reason: \\"Please use the explicit _SET field\\") - testId_SET: String - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + testId: StringScalarMutations + testId_SET: String @deprecated(reason: \\"Please use the generic mutation 'testId: { set: ... } }' instead.\\") + year: IntScalarMutations + year_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { decrement: ... } }' instead.\\") + year_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { increment: ... } }' instead.\\") + year_SET: Int @deprecated(reason: \\"Please use the generic mutation 'year: { set: ... } }' instead.\\") } input movieWhere { AND: [movieWhere!] NOT: movieWhere OR: [movieWhere!] + actors: actorRelationshipFilters actorsAggregate: movieActorsAggregateInput + actorsConnection: movieActorsConnectionFilters \\"\\"\\" Return movies where all of the related movieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: movieActorsConnectionWhere + actorsConnection_ALL: movieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return movies where none of the related movieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: movieActorsConnectionWhere + actorsConnection_NONE: movieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return movies where one of the related movieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: movieActorsConnectionWhere + actorsConnection_SINGLE: movieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return movies where some of the related movieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: movieActorsConnectionWhere + actorsConnection_SOME: movieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return movies where all of the related actors match this filter\\"\\"\\" - actors_ALL: actorWhere + actors_ALL: actorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return movies where none of the related actors match this filter\\"\\"\\" - actors_NONE: actorWhere + actors_NONE: actorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return movies where one of the related actors match this filter\\"\\"\\" - actors_SINGLE: actorWhere + actors_SINGLE: actorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return movies where some of the related actors match this filter\\"\\"\\" - actors_SOME: actorWhere - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime] - createdAt_LT: DateTime - createdAt_LTE: DateTime - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - testId: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - testId_CONTAINS: String - testId_ENDS_WITH: String - testId_EQ: String - testId_IN: [String] - testId_STARTS_WITH: String - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int] - year_LT: Int - year_LTE: Int + actors_SOME: actorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + testId: StringScalarFilters + testId_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter testId: { contains: ... }\\") + testId_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter testId: { endsWith: ... }\\") + testId_EQ: String @deprecated(reason: \\"Please use the relevant generic filter testId: { eq: ... }\\") + testId_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter testId: { in: ... }\\") + testId_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter testId: { startsWith: ... }\\") + year: IntScalarFilters + year_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter year: { eq: ... }\\") + year_GT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gt: ... }\\") + year_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gte: ... }\\") + year_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter year: { in: ... }\\") + year_LT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lt: ... }\\") + year_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lte: ... }\\") } type movieactorActorsAggregationSelection { diff --git a/packages/graphql/tests/schema/math.test.ts b/packages/graphql/tests/schema/math.test.ts index 63949ea0ea..b0fc8b0ecc 100644 --- a/packages/graphql/tests/schema/math.test.ts +++ b/packages/graphql/tests/schema/math.test.ts @@ -59,9 +59,18 @@ describe("Algebraic", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -71,6 +80,23 @@ describe("Algebraic", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { id: ID viewers: Int! @@ -78,7 +104,6 @@ describe("Algebraic", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: IntAggregateSelection! } @@ -92,15 +117,6 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -110,31 +126,31 @@ describe("Algebraic", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_DECREMENT: Int - viewers_INCREMENT: Int - viewers_SET: Int + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + viewers: IntScalarMutations + viewers_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { decrement: ... } }' instead.\\") + viewers_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { increment: ... } }' instead.\\") + viewers_SET: Int @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: Int - viewers_GT: Int - viewers_GTE: Int - viewers_IN: [Int!] - viewers_LT: Int - viewers_LTE: Int + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + viewers: IntScalarFilters + viewers_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") } type MoviesConnection { @@ -158,7 +174,7 @@ describe("Algebraic", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -215,6 +231,23 @@ describe("Algebraic", () => { sum: BigInt } + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -236,9 +269,18 @@ describe("Algebraic", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -248,7 +290,6 @@ describe("Algebraic", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: BigIntAggregateSelection! } @@ -262,15 +303,6 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -280,31 +312,31 @@ describe("Algebraic", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - viewers: BigInt @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_DECREMENT: BigInt - viewers_INCREMENT: BigInt - viewers_SET: BigInt + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + viewers: BigIntScalarMutations + viewers_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { decrement: ... } }' instead.\\") + viewers_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { increment: ... } }' instead.\\") + viewers_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - viewers: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: BigInt - viewers_GT: BigInt - viewers_GTE: BigInt - viewers_IN: [BigInt!] - viewers_LT: BigInt - viewers_LTE: BigInt + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + viewers: BigIntScalarFilters + viewers_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [BigInt!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") } type MoviesConnection { @@ -328,7 +360,7 @@ describe("Algebraic", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -402,9 +434,37 @@ describe("Algebraic", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -414,7 +474,6 @@ describe("Algebraic", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: FloatAggregateSelection! } @@ -428,15 +487,6 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -446,33 +496,33 @@ describe("Algebraic", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - viewers: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_ADD: Float - viewers_DIVIDE: Float - viewers_MULTIPLY: Float - viewers_SET: Float - viewers_SUBTRACT: Float + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + viewers: FloatScalarMutations + viewers_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { add: ... } }' instead.\\") + viewers_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { divide: ... } }' instead.\\") + viewers_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { multiply: ... } }' instead.\\") + viewers_SET: Float @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") + viewers_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { subtract: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - viewers: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: Float - viewers_GT: Float - viewers_GTE: Float - viewers_IN: [Float!] - viewers_LT: Float - viewers_LTE: Float + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + viewers: FloatScalarFilters + viewers_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: Float @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: Float @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") } type MoviesConnection { @@ -496,7 +546,7 @@ describe("Algebraic", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -531,7 +581,7 @@ describe("Algebraic", () => { type Movie @node { id: ID viewers: Int! - directedBy: Director @relationship(type: "DIRECTS", direction: IN) + directedBy: [Director!]! @relationship(type: "DIRECTS", direction: IN) } type Director @node { @@ -574,9 +624,9 @@ describe("Algebraic", () => { } type Director { - directs(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - directsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): DirectorMovieDirectsAggregationSelection - directsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [DirectorDirectsConnectionSort!], where: DirectorDirectsConnectionWhere): DirectorDirectsConnection! + directs(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + directsAggregate(where: MovieWhere): DirectorMovieDirectsAggregationSelection + directsConnection(after: String, first: Int, sort: [DirectorDirectsConnectionSort!], where: DirectorDirectsConnectionWhere): DirectorDirectsConnection! lastName: String! } @@ -606,7 +656,7 @@ describe("Algebraic", () => { AND: [DirectorDirectsAggregateInput!] NOT: DirectorDirectsAggregateInput OR: [DirectorDirectsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -617,10 +667,6 @@ describe("Algebraic", () => { input DirectorDirectsConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -630,6 +676,25 @@ describe("Algebraic", () => { totalCount: Int! } + input DirectorDirectsConnectionFilters { + \\"\\"\\" + Return Directors where all of the related DirectorDirectsConnections match this filter + \\"\\"\\" + all: DirectorDirectsConnectionWhere + \\"\\"\\" + Return Directors where none of the related DirectorDirectsConnections match this filter + \\"\\"\\" + none: DirectorDirectsConnectionWhere + \\"\\"\\" + Return Directors where one of the related DirectorDirectsConnections match this filter + \\"\\"\\" + single: DirectorDirectsConnectionWhere + \\"\\"\\" + Return Directors where some of the related DirectorDirectsConnections match this filter + \\"\\"\\" + some: DirectorDirectsConnectionWhere + } + input DirectorDirectsConnectionSort { node: MovieSort } @@ -664,36 +729,27 @@ describe("Algebraic", () => { AND: [DirectorDirectsNodeAggregationWhereInput!] NOT: DirectorDirectsNodeAggregationWhereInput OR: [DirectorDirectsNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - viewers_AVERAGE_EQUAL: Float - viewers_AVERAGE_GT: Float - viewers_AVERAGE_GTE: Float - viewers_AVERAGE_LT: Float - viewers_AVERAGE_LTE: Float - viewers_MAX_EQUAL: Int - viewers_MAX_GT: Int - viewers_MAX_GTE: Int - viewers_MAX_LT: Int - viewers_MAX_LTE: Int - viewers_MIN_EQUAL: Int - viewers_MIN_GT: Int - viewers_MIN_GTE: Int - viewers_MIN_LT: Int - viewers_MIN_LTE: Int - viewers_SUM_EQUAL: Int - viewers_SUM_GT: Int - viewers_SUM_GTE: Int - viewers_SUM_LT: Int - viewers_SUM_LTE: Int + viewers: IntScalarAggregationFilters + viewers_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { eq: ... } } }' instead.\\") + viewers_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { gt: ... } } }' instead.\\") + viewers_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { gte: ... } } }' instead.\\") + viewers_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { lt: ... } } }' instead.\\") + viewers_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { lte: ... } } }' instead.\\") + viewers_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { eq: ... } } }' instead.\\") + viewers_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { gt: ... } } }' instead.\\") + viewers_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { gte: ... } } }' instead.\\") + viewers_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { lt: ... } } }' instead.\\") + viewers_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { lte: ... } } }' instead.\\") + viewers_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { eq: ... } } }' instead.\\") + viewers_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { gt: ... } } }' instead.\\") + viewers_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { gte: ... } } }' instead.\\") + viewers_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { lt: ... } } }' instead.\\") + viewers_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { lte: ... } } }' instead.\\") + viewers_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { eq: ... } } }' instead.\\") + viewers_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { gt: ... } } }' instead.\\") + viewers_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { gte: ... } } }' instead.\\") + viewers_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { lt: ... } } }' instead.\\") + viewers_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { lte: ... } } }' instead.\\") } type DirectorDirectsRelationship { @@ -729,17 +785,18 @@ describe("Algebraic", () => { } type DirectorMovieDirectsNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: IntAggregateSelection! } - input DirectorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more DirectorSort objects to sort Directors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [DirectorSort!] + input DirectorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Directors match this filter\\"\\"\\" + all: DirectorWhere + \\"\\"\\"Filter type where none of the related Directors match this filter\\"\\"\\" + none: DirectorWhere + \\"\\"\\"Filter type where one of the related Directors match this filter\\"\\"\\" + single: DirectorWhere + \\"\\"\\"Filter type where some of the related Directors match this filter\\"\\"\\" + some: DirectorWhere } \\"\\"\\" @@ -751,45 +808,47 @@ describe("Algebraic", () => { input DirectorUpdateInput { directs: [DirectorDirectsUpdateFieldInput!] - lastName: String @deprecated(reason: \\"Please use the explicit _SET field\\") - lastName_SET: String + lastName: StringScalarMutations + lastName_SET: String @deprecated(reason: \\"Please use the generic mutation 'lastName: { set: ... } }' instead.\\") } input DirectorWhere { AND: [DirectorWhere!] NOT: DirectorWhere OR: [DirectorWhere!] + directs: MovieRelationshipFilters directsAggregate: DirectorDirectsAggregateInput + directsConnection: DirectorDirectsConnectionFilters \\"\\"\\" Return Directors where all of the related DirectorDirectsConnections match this filter \\"\\"\\" - directsConnection_ALL: DirectorDirectsConnectionWhere + directsConnection_ALL: DirectorDirectsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Directors where none of the related DirectorDirectsConnections match this filter \\"\\"\\" - directsConnection_NONE: DirectorDirectsConnectionWhere + directsConnection_NONE: DirectorDirectsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Directors where one of the related DirectorDirectsConnections match this filter \\"\\"\\" - directsConnection_SINGLE: DirectorDirectsConnectionWhere + directsConnection_SINGLE: DirectorDirectsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Directors where some of the related DirectorDirectsConnections match this filter \\"\\"\\" - directsConnection_SOME: DirectorDirectsConnectionWhere + directsConnection_SOME: DirectorDirectsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Directors where all of the related Movies match this filter\\"\\"\\" - directs_ALL: MovieWhere + directs_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'directs: { all: ... }' instead.\\") \\"\\"\\"Return Directors where none of the related Movies match this filter\\"\\"\\" - directs_NONE: MovieWhere + directs_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'directs: { none: ... }' instead.\\") \\"\\"\\"Return Directors where one of the related Movies match this filter\\"\\"\\" - directs_SINGLE: MovieWhere + directs_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'directs: { single: ... }' instead.\\") \\"\\"\\"Return Directors where some of the related Movies match this filter\\"\\"\\" - directs_SOME: MovieWhere - lastName: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - lastName_CONTAINS: String - lastName_ENDS_WITH: String - lastName_EQ: String - lastName_IN: [String!] - lastName_STARTS_WITH: String + directs_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'directs: { some: ... }' instead.\\") + lastName: StringScalarFilters + lastName_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter lastName: { contains: ... }\\") + lastName_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter lastName: { endsWith: ... }\\") + lastName_EQ: String @deprecated(reason: \\"Please use the relevant generic filter lastName: { eq: ... }\\") + lastName_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter lastName: { in: ... }\\") + lastName_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter lastName: { startsWith: ... }\\") } type DirectorsConnection { @@ -798,9 +857,28 @@ describe("Algebraic", () => { totalCount: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -810,22 +888,46 @@ describe("Algebraic", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - directedBy(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: DirectorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [DirectorSort!], where: DirectorWhere): Director - directedByAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: DirectorWhere): MovieDirectorDirectedByAggregationSelection - directedByConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieDirectedByConnectionSort!], where: MovieDirectedByConnectionWhere): MovieDirectedByConnection! + directedBy(limit: Int, offset: Int, sort: [DirectorSort!], where: DirectorWhere): [Director!]! + directedByAggregate(where: DirectorWhere): MovieDirectorDirectedByAggregationSelection + directedByConnection(after: String, first: Int, sort: [MovieDirectedByConnectionSort!], where: MovieDirectedByConnectionWhere): MovieDirectedByConnection! id: ID viewers: Int! } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: IntAggregateSelection! } input MovieConnectInput { - directedBy: MovieDirectedByConnectFieldInput + directedBy: [MovieDirectedByConnectFieldInput!] } input MovieConnectWhere { @@ -839,14 +941,14 @@ describe("Algebraic", () => { } input MovieDeleteInput { - directedBy: MovieDirectedByDeleteFieldInput + directedBy: [MovieDirectedByDeleteFieldInput!] } input MovieDirectedByAggregateInput { AND: [MovieDirectedByAggregateInput!] NOT: MovieDirectedByAggregateInput OR: [MovieDirectedByAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -856,11 +958,7 @@ describe("Algebraic", () => { } input MovieDirectedByConnectFieldInput { - connect: DirectorConnectInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") + connect: [DirectorConnectInput!] where: DirectorConnectWhere } @@ -870,6 +968,25 @@ describe("Algebraic", () => { totalCount: Int! } + input MovieDirectedByConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieDirectedByConnections match this filter + \\"\\"\\" + all: MovieDirectedByConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieDirectedByConnections match this filter + \\"\\"\\" + none: MovieDirectedByConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieDirectedByConnections match this filter + \\"\\"\\" + single: MovieDirectedByConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieDirectedByConnections match this filter + \\"\\"\\" + some: MovieDirectedByConnectionWhere + } + input MovieDirectedByConnectionSort { node: DirectorSort } @@ -896,29 +1013,30 @@ describe("Algebraic", () => { } input MovieDirectedByFieldInput { - connect: MovieDirectedByConnectFieldInput - create: MovieDirectedByCreateFieldInput + connect: [MovieDirectedByConnectFieldInput!] + create: [MovieDirectedByCreateFieldInput!] } input MovieDirectedByNodeAggregationWhereInput { AND: [MovieDirectedByNodeAggregationWhereInput!] NOT: MovieDirectedByNodeAggregationWhereInput OR: [MovieDirectedByNodeAggregationWhereInput!] - lastName_AVERAGE_LENGTH_EQUAL: Float - lastName_AVERAGE_LENGTH_GT: Float - lastName_AVERAGE_LENGTH_GTE: Float - lastName_AVERAGE_LENGTH_LT: Float - lastName_AVERAGE_LENGTH_LTE: Float - lastName_LONGEST_LENGTH_EQUAL: Int - lastName_LONGEST_LENGTH_GT: Int - lastName_LONGEST_LENGTH_GTE: Int - lastName_LONGEST_LENGTH_LT: Int - lastName_LONGEST_LENGTH_LTE: Int - lastName_SHORTEST_LENGTH_EQUAL: Int - lastName_SHORTEST_LENGTH_GT: Int - lastName_SHORTEST_LENGTH_GTE: Int - lastName_SHORTEST_LENGTH_LT: Int - lastName_SHORTEST_LENGTH_LTE: Int + lastName: StringScalarAggregationFilters + lastName_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { averageLength: { eq: ... } } }' instead.\\") + lastName_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { averageLength: { gt: ... } } }' instead.\\") + lastName_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { averageLength: { gte: ... } } }' instead.\\") + lastName_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { averageLength: { lt: ... } } }' instead.\\") + lastName_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { averageLength: { lte: ... } } }' instead.\\") + lastName_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { longestLength: { eq: ... } } }' instead.\\") + lastName_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { longestLength: { gt: ... } } }' instead.\\") + lastName_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { longestLength: { gte: ... } } }' instead.\\") + lastName_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { longestLength: { lt: ... } } }' instead.\\") + lastName_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { longestLength: { lte: ... } } }' instead.\\") + lastName_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { shortestLength: { eq: ... } } }' instead.\\") + lastName_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { shortestLength: { gt: ... } } }' instead.\\") + lastName_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { shortestLength: { gte: ... } } }' instead.\\") + lastName_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { shortestLength: { lt: ... } } }' instead.\\") + lastName_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'lastName: { shortestLength: { lte: ... } } }' instead.\\") } type MovieDirectedByRelationship { @@ -931,10 +1049,10 @@ describe("Algebraic", () => { } input MovieDirectedByUpdateFieldInput { - connect: MovieDirectedByConnectFieldInput - create: MovieDirectedByCreateFieldInput - delete: MovieDirectedByDeleteFieldInput - disconnect: MovieDirectedByDisconnectFieldInput + connect: [MovieDirectedByConnectFieldInput!] + create: [MovieDirectedByCreateFieldInput!] + delete: [MovieDirectedByDeleteFieldInput!] + disconnect: [MovieDirectedByDisconnectFieldInput!] update: MovieDirectedByUpdateConnectionInput where: MovieDirectedByConnectionWhere } @@ -949,7 +1067,7 @@ describe("Algebraic", () => { } input MovieDisconnectInput { - directedBy: MovieDirectedByDisconnectFieldInput + directedBy: [MovieDirectedByDisconnectFieldInput!] } type MovieEdge { @@ -957,13 +1075,15 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -975,35 +1095,59 @@ describe("Algebraic", () => { } input MovieUpdateInput { - directedBy: MovieDirectedByUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_DECREMENT: Int - viewers_INCREMENT: Int - viewers_SET: Int + directedBy: [MovieDirectedByUpdateFieldInput!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + viewers: IntScalarMutations + viewers_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { decrement: ... } }' instead.\\") + viewers_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { increment: ... } }' instead.\\") + viewers_SET: Int @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - directedBy: DirectorWhere + directedBy: DirectorRelationshipFilters directedByAggregate: MovieDirectedByAggregateInput - directedByConnection: MovieDirectedByConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: Int - viewers_GT: Int - viewers_GTE: Int - viewers_IN: [Int!] - viewers_LT: Int - viewers_LTE: Int + directedByConnection: MovieDirectedByConnectionFilters + \\"\\"\\" + Return Movies where all of the related MovieDirectedByConnections match this filter + \\"\\"\\" + directedByConnection_ALL: MovieDirectedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedByConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related MovieDirectedByConnections match this filter + \\"\\"\\" + directedByConnection_NONE: MovieDirectedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedByConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related MovieDirectedByConnections match this filter + \\"\\"\\" + directedByConnection_SINGLE: MovieDirectedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedByConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related MovieDirectedByConnections match this filter + \\"\\"\\" + directedByConnection_SOME: MovieDirectedByConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedByConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Directors match this filter\\"\\"\\" + directedBy_ALL: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedBy: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Directors match this filter\\"\\"\\" + directedBy_NONE: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedBy: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Directors match this filter\\"\\"\\" + directedBy_SINGLE: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedBy: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Directors match this filter\\"\\"\\" + directedBy_SOME: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directedBy: { some: ... }' instead.\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + viewers: IntScalarFilters + viewers_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") } type MoviesConnection { @@ -1030,10 +1174,10 @@ describe("Algebraic", () => { } type Query { - directors(limit: Int, offset: Int, options: DirectorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [DirectorSort!], where: DirectorWhere): [Director!]! + directors(limit: Int, offset: Int, sort: [DirectorSort!], where: DirectorWhere): [Director!]! directorsAggregate(where: DirectorWhere): DirectorAggregateSelection! directorsConnection(after: String, first: Int, sort: [DirectorSort!], where: DirectorWhere): DirectorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1051,6 +1195,27 @@ describe("Algebraic", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateDirectorsMutationResponse { directors: [Director!]! info: UpdateInfo! @@ -1124,9 +1289,28 @@ describe("Algebraic", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -1136,17 +1320,41 @@ describe("Algebraic", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie implements Production { id: ID viewers: Int! - workers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - workersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonWorkersAggregationSelection - workersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieWorkersConnectionSort!], where: MovieWorkersConnectionWhere): MovieWorkersConnection! + workers(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + workersAggregate(where: PersonWhere): MoviePersonWorkersAggregationSelection + workersConnection(after: String, first: Int, sort: [MovieWorkersConnectionSort!], where: MovieWorkersConnectionWhere): MovieWorkersConnection! } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") viewers: IntAggregateSelection! } @@ -1165,15 +1373,6 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonWorkersAggregationSelection { count: Int! node: MoviePersonWorkersNodeAggregateSelection @@ -1192,12 +1391,12 @@ describe("Algebraic", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_DECREMENT: Int - viewers_INCREMENT: Int - viewers_SET: Int + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + viewers: IntScalarMutations + viewers_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { decrement: ... } }' instead.\\") + viewers_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { increment: ... } }' instead.\\") + viewers_SET: Int @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") workers: [MovieWorkersUpdateFieldInput!] } @@ -1205,51 +1404,53 @@ describe("Algebraic", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - viewers: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: Int - viewers_GT: Int - viewers_GTE: Int - viewers_IN: [Int!] - viewers_LT: Int - viewers_LTE: Int + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + viewers: IntScalarFilters + viewers_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") + workers: PersonRelationshipFilters workersAggregate: MovieWorkersAggregateInput + workersConnection: MovieWorkersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieWorkersConnections match this filter \\"\\"\\" - workersConnection_ALL: MovieWorkersConnectionWhere + workersConnection_ALL: MovieWorkersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'workersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieWorkersConnections match this filter \\"\\"\\" - workersConnection_NONE: MovieWorkersConnectionWhere + workersConnection_NONE: MovieWorkersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'workersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieWorkersConnections match this filter \\"\\"\\" - workersConnection_SINGLE: MovieWorkersConnectionWhere + workersConnection_SINGLE: MovieWorkersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'workersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieWorkersConnections match this filter \\"\\"\\" - workersConnection_SOME: MovieWorkersConnectionWhere + workersConnection_SOME: MovieWorkersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'workersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - workers_ALL: PersonWhere + workers_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'workers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - workers_NONE: PersonWhere + workers_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'workers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - workers_SINGLE: PersonWhere + workers_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'workers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - workers_SOME: PersonWhere + workers_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'workers: { some: ... }' instead.\\") } input MovieWorkersAggregateInput { AND: [MovieWorkersAggregateInput!] NOT: MovieWorkersAggregateInput OR: [MovieWorkersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1260,10 +1461,6 @@ describe("Algebraic", () => { input MovieWorkersConnectFieldInput { connect: [PersonConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PersonConnectWhere } @@ -1273,6 +1470,25 @@ describe("Algebraic", () => { totalCount: Int! } + input MovieWorkersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieWorkersConnections match this filter + \\"\\"\\" + all: MovieWorkersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieWorkersConnections match this filter + \\"\\"\\" + none: MovieWorkersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieWorkersConnections match this filter + \\"\\"\\" + single: MovieWorkersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieWorkersConnections match this filter + \\"\\"\\" + some: MovieWorkersConnectionWhere + } + input MovieWorkersConnectionSort { node: PersonSort } @@ -1307,21 +1523,22 @@ describe("Algebraic", () => { AND: [MovieWorkersNodeAggregationWhereInput!] NOT: MovieWorkersNodeAggregationWhereInput OR: [MovieWorkersNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieWorkersRelationship { @@ -1373,9 +1590,9 @@ describe("Algebraic", () => { type Person { name: String! - worksInProduction(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - worksInProductionAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): PersonProductionWorksInProductionAggregationSelection - worksInProductionConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonWorksInProductionConnectionSort!], where: PersonWorksInProductionConnectionWhere): PersonWorksInProductionConnection! + worksInProduction(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! + worksInProductionAggregate(where: ProductionWhere): PersonProductionWorksInProductionAggregationSelection + worksInProductionConnection(after: String, first: Int, sort: [PersonWorksInProductionConnectionSort!], where: PersonWorksInProductionConnectionWhere): PersonWorksInProductionConnection! } type PersonAggregateSelection { @@ -1409,15 +1626,6 @@ describe("Algebraic", () => { node: Person! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - type PersonProductionWorksInProductionAggregationSelection { count: Int! node: PersonProductionWorksInProductionNodeAggregateSelection @@ -1427,6 +1635,17 @@ describe("Algebraic", () => { viewers: IntAggregateSelection! } + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere + } + \\"\\"\\" Fields to sort People by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonSort object. \\"\\"\\" @@ -1435,8 +1654,8 @@ describe("Algebraic", () => { } input PersonUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") worksInProduction: [PersonWorksInProductionUpdateFieldInput!] } @@ -1444,44 +1663,46 @@ describe("Algebraic", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + worksInProduction: ProductionRelationshipFilters worksInProductionAggregate: PersonWorksInProductionAggregateInput + worksInProductionConnection: PersonWorksInProductionConnectionFilters \\"\\"\\" Return People where all of the related PersonWorksInProductionConnections match this filter \\"\\"\\" - worksInProductionConnection_ALL: PersonWorksInProductionConnectionWhere + worksInProductionConnection_ALL: PersonWorksInProductionConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProductionConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonWorksInProductionConnections match this filter \\"\\"\\" - worksInProductionConnection_NONE: PersonWorksInProductionConnectionWhere + worksInProductionConnection_NONE: PersonWorksInProductionConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProductionConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonWorksInProductionConnections match this filter \\"\\"\\" - worksInProductionConnection_SINGLE: PersonWorksInProductionConnectionWhere + worksInProductionConnection_SINGLE: PersonWorksInProductionConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProductionConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonWorksInProductionConnections match this filter \\"\\"\\" - worksInProductionConnection_SOME: PersonWorksInProductionConnectionWhere + worksInProductionConnection_SOME: PersonWorksInProductionConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProductionConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related Productions match this filter\\"\\"\\" - worksInProduction_ALL: ProductionWhere + worksInProduction_ALL: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProduction: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related Productions match this filter\\"\\"\\" - worksInProduction_NONE: ProductionWhere + worksInProduction_NONE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProduction: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related Productions match this filter\\"\\"\\" - worksInProduction_SINGLE: ProductionWhere + worksInProduction_SINGLE: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProduction: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related Productions match this filter\\"\\"\\" - worksInProduction_SOME: ProductionWhere + worksInProduction_SOME: ProductionWhere @deprecated(reason: \\"Please use the relevant generic filter 'worksInProduction: { some: ... }' instead.\\") } input PersonWorksInProductionAggregateInput { AND: [PersonWorksInProductionAggregateInput!] NOT: PersonWorksInProductionAggregateInput OR: [PersonWorksInProductionAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1500,6 +1721,25 @@ describe("Algebraic", () => { totalCount: Int! } + input PersonWorksInProductionConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonWorksInProductionConnections match this filter + \\"\\"\\" + all: PersonWorksInProductionConnectionWhere + \\"\\"\\" + Return People where none of the related PersonWorksInProductionConnections match this filter + \\"\\"\\" + none: PersonWorksInProductionConnectionWhere + \\"\\"\\" + Return People where one of the related PersonWorksInProductionConnections match this filter + \\"\\"\\" + single: PersonWorksInProductionConnectionWhere + \\"\\"\\" + Return People where some of the related PersonWorksInProductionConnections match this filter + \\"\\"\\" + some: PersonWorksInProductionConnectionWhere + } + input PersonWorksInProductionConnectionSort { node: ProductionSort } @@ -1532,26 +1772,27 @@ describe("Algebraic", () => { AND: [PersonWorksInProductionNodeAggregationWhereInput!] NOT: PersonWorksInProductionNodeAggregationWhereInput OR: [PersonWorksInProductionNodeAggregationWhereInput!] - viewers_AVERAGE_EQUAL: Float - viewers_AVERAGE_GT: Float - viewers_AVERAGE_GTE: Float - viewers_AVERAGE_LT: Float - viewers_AVERAGE_LTE: Float - viewers_MAX_EQUAL: Int - viewers_MAX_GT: Int - viewers_MAX_GTE: Int - viewers_MAX_LT: Int - viewers_MAX_LTE: Int - viewers_MIN_EQUAL: Int - viewers_MIN_GT: Int - viewers_MIN_GTE: Int - viewers_MIN_LT: Int - viewers_MIN_LTE: Int - viewers_SUM_EQUAL: Int - viewers_SUM_GT: Int - viewers_SUM_GTE: Int - viewers_SUM_LT: Int - viewers_SUM_LTE: Int + viewers: IntScalarAggregationFilters + viewers_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { eq: ... } } }' instead.\\") + viewers_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { gt: ... } } }' instead.\\") + viewers_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { gte: ... } } }' instead.\\") + viewers_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { lt: ... } } }' instead.\\") + viewers_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { average: { lte: ... } } }' instead.\\") + viewers_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { eq: ... } } }' instead.\\") + viewers_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { gt: ... } } }' instead.\\") + viewers_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { gte: ... } } }' instead.\\") + viewers_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { lt: ... } } }' instead.\\") + viewers_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { max: { lte: ... } } }' instead.\\") + viewers_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { eq: ... } } }' instead.\\") + viewers_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { gt: ... } } }' instead.\\") + viewers_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { gte: ... } } }' instead.\\") + viewers_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { lt: ... } } }' instead.\\") + viewers_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { min: { lte: ... } } }' instead.\\") + viewers_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { eq: ... } } }' instead.\\") + viewers_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { gt: ... } } }' instead.\\") + viewers_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { gte: ... } } }' instead.\\") + viewers_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { lt: ... } } }' instead.\\") + viewers_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'viewers: { sum: { lte: ... } } }' instead.\\") } type PersonWorksInProductionRelationship { @@ -1598,13 +1839,15 @@ describe("Algebraic", () => { Movie } - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] + input ProductionRelationshipFilters { + \\"\\"\\"Filter type where all of the related Productions match this filter\\"\\"\\" + all: ProductionWhere + \\"\\"\\"Filter type where none of the related Productions match this filter\\"\\"\\" + none: ProductionWhere + \\"\\"\\"Filter type where one of the related Productions match this filter\\"\\"\\" + single: ProductionWhere + \\"\\"\\"Filter type where some of the related Productions match this filter\\"\\"\\" + some: ProductionWhere } \\"\\"\\" @@ -1615,10 +1858,10 @@ describe("Algebraic", () => { } input ProductionUpdateInput { - viewers: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - viewers_DECREMENT: Int - viewers_INCREMENT: Int - viewers_SET: Int + viewers: IntScalarMutations + viewers_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { decrement: ... } }' instead.\\") + viewers_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'viewers: { increment: ... } }' instead.\\") + viewers_SET: Int @deprecated(reason: \\"Please use the generic mutation 'viewers: { set: ... } }' instead.\\") } input ProductionWhere { @@ -1626,14 +1869,13 @@ describe("Algebraic", () => { NOT: ProductionWhere OR: [ProductionWhere!] typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - viewers: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - viewers_EQ: Int - viewers_GT: Int - viewers_GTE: Int - viewers_IN: [Int!] - viewers_LT: Int - viewers_LTE: Int + viewers: IntScalarFilters + viewers_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { eq: ... }\\") + viewers_GT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gt: ... }\\") + viewers_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { gte: ... }\\") + viewers_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter viewers: { in: ... }\\") + viewers_LT: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lt: ... }\\") + viewers_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter viewers: { lte: ... }\\") } type ProductionsConnection { @@ -1643,13 +1885,13 @@ describe("Algebraic", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! + productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! } @@ -1667,6 +1909,27 @@ describe("Algebraic", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -1729,26 +1992,27 @@ describe("Algebraic", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - pay_AVERAGE_EQUAL: Float - pay_AVERAGE_GT: Float - pay_AVERAGE_GTE: Float - pay_AVERAGE_LT: Float - pay_AVERAGE_LTE: Float - pay_MAX_EQUAL: Float - pay_MAX_GT: Float - pay_MAX_GTE: Float - pay_MAX_LT: Float - pay_MAX_LTE: Float - pay_MIN_EQUAL: Float - pay_MIN_GT: Float - pay_MIN_GTE: Float - pay_MIN_LT: Float - pay_MIN_LTE: Float - pay_SUM_EQUAL: Float - pay_SUM_GT: Float - pay_SUM_GTE: Float - pay_SUM_LT: Float - pay_SUM_LTE: Float + pay: FloatScalarAggregationFilters + pay_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { average: { eq: ... } } }' instead.\\") + pay_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { average: { gt: ... } } }' instead.\\") + pay_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { average: { gte: ... } } }' instead.\\") + pay_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { average: { lt: ... } } }' instead.\\") + pay_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { average: { lte: ... } } }' instead.\\") + pay_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { max: { eq: ... } } }' instead.\\") + pay_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { max: { gt: ... } } }' instead.\\") + pay_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { max: { gte: ... } } }' instead.\\") + pay_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { max: { lt: ... } } }' instead.\\") + pay_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { max: { lte: ... } } }' instead.\\") + pay_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { min: { eq: ... } } }' instead.\\") + pay_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { min: { gt: ... } } }' instead.\\") + pay_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { min: { gte: ... } } }' instead.\\") + pay_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { min: { lt: ... } } }' instead.\\") + pay_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { min: { lte: ... } } }' instead.\\") + pay_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { sum: { eq: ... } } }' instead.\\") + pay_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { sum: { gt: ... } } }' instead.\\") + pay_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { sum: { gte: ... } } }' instead.\\") + pay_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { sum: { lt: ... } } }' instead.\\") + pay_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'pay: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -1762,32 +2026,32 @@ describe("Algebraic", () => { } input ActedInUpdateInput { - pay: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - pay_ADD: Float - pay_DIVIDE: Float - pay_MULTIPLY: Float - pay_SET: Float - pay_SUBTRACT: Float - roles: [String!] @deprecated(reason: \\"Please use the explicit _SET field\\") - roles_POP: Int - roles_PUSH: [String!] - roles_SET: [String!] + pay: FloatScalarMutations + pay_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'pay: { add: ... } }' instead.\\") + pay_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'pay: { divide: ... } }' instead.\\") + pay_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'pay: { multiply: ... } }' instead.\\") + pay_SET: Float @deprecated(reason: \\"Please use the generic mutation 'pay: { set: ... } }' instead.\\") + pay_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'pay: { subtract: ... } }' instead.\\") + roles: ListStringMutations + roles_POP: Int @deprecated(reason: \\"Please use the generic mutation 'roles: { pop: ... } }' instead.\\") + roles_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'roles: { push: ... } }' instead.\\") + roles_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'roles: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - pay: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - pay_EQ: Float - pay_GT: Float - pay_GTE: Float - pay_IN: [Float] - pay_LT: Float - pay_LTE: Float - roles: [String!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - roles_EQ: [String!] - roles_INCLUDES: String + pay: FloatScalarFilters + pay_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { eq: ... }\\") + pay_GT: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { gt: ... }\\") + pay_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { gte: ... }\\") + pay_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter pay: { in: ... }\\") + pay_LT: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { lt: ... }\\") + pay_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { lte: ... }\\") + roles: StringListFilters + roles_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter roles: { eq: ... }\\") + roles_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter roles: { includes: ... }\\") } \\"\\"\\" @@ -1823,10 +2087,54 @@ describe("Algebraic", () => { sum: Float } + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: PersonWhere): MoviePersonActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! + actorsAggregate(where: PersonWhere): MoviePersonActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String! } @@ -1834,7 +2142,7 @@ describe("Algebraic", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1847,10 +2155,6 @@ describe("Algebraic", () => { input MovieActorsConnectFieldInput { connect: [PersonConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: PersonConnectWhere } @@ -1860,6 +2164,25 @@ describe("Algebraic", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: PersonSort @@ -1897,21 +2220,22 @@ describe("Algebraic", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -1965,15 +2289,6 @@ describe("Algebraic", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - type MoviePersonActorsAggregationSelection { count: Int! edge: MoviePersonActorsEdgeAggregateSelection @@ -1988,6 +2303,17 @@ describe("Algebraic", () => { name: StringAggregateSelection! } + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -1997,45 +2323,47 @@ describe("Algebraic", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: PersonRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related People match this filter\\"\\"\\" - actors_ALL: PersonWhere + actors_ALL: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related People match this filter\\"\\"\\" - actors_NONE: PersonWhere + actors_NONE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related People match this filter\\"\\"\\" - actors_SINGLE: PersonWhere + actors_SINGLE: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related People match this filter\\"\\"\\" - actors_SOME: PersonWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + actors_SOME: PersonWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -2068,9 +2396,9 @@ describe("Algebraic", () => { } type Person { - actedInMovies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInMoviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): PersonMovieActedInMoviesAggregationSelection - actedInMoviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonActedInMoviesConnectionSort!], where: PersonActedInMoviesConnectionWhere): PersonActedInMoviesConnection! + actedInMovies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInMoviesAggregate(where: MovieWhere): PersonMovieActedInMoviesAggregationSelection + actedInMoviesConnection(after: String, first: Int, sort: [PersonActedInMoviesConnectionSort!], where: PersonActedInMoviesConnectionWhere): PersonActedInMoviesConnection! name: String! } @@ -2078,7 +2406,7 @@ describe("Algebraic", () => { AND: [PersonActedInMoviesAggregateInput!] NOT: PersonActedInMoviesAggregateInput OR: [PersonActedInMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2091,10 +2419,6 @@ describe("Algebraic", () => { input PersonActedInMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -2104,6 +2428,25 @@ describe("Algebraic", () => { totalCount: Int! } + input PersonActedInMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonActedInMoviesConnections match this filter + \\"\\"\\" + all: PersonActedInMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related PersonActedInMoviesConnections match this filter + \\"\\"\\" + none: PersonActedInMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related PersonActedInMoviesConnections match this filter + \\"\\"\\" + single: PersonActedInMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related PersonActedInMoviesConnections match this filter + \\"\\"\\" + some: PersonActedInMoviesConnectionWhere + } + input PersonActedInMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -2141,21 +2484,22 @@ describe("Algebraic", () => { AND: [PersonActedInMoviesNodeAggregationWhereInput!] NOT: PersonActedInMoviesNodeAggregationWhereInput OR: [PersonActedInMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type PersonActedInMoviesRelationship { @@ -2223,13 +2567,15 @@ describe("Algebraic", () => { title: StringAggregateSelection! } - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] + input PersonRelationshipFilters { + \\"\\"\\"Filter type where all of the related People match this filter\\"\\"\\" + all: PersonWhere + \\"\\"\\"Filter type where none of the related People match this filter\\"\\"\\" + none: PersonWhere + \\"\\"\\"Filter type where one of the related People match this filter\\"\\"\\" + single: PersonWhere + \\"\\"\\"Filter type where some of the related People match this filter\\"\\"\\" + some: PersonWhere } \\"\\"\\" @@ -2241,52 +2587,54 @@ describe("Algebraic", () => { input PersonUpdateInput { actedInMovies: [PersonActedInMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] + actedInMovies: MovieRelationshipFilters actedInMoviesAggregate: PersonActedInMoviesAggregateInput + actedInMoviesConnection: PersonActedInMoviesConnectionFilters \\"\\"\\" Return People where all of the related PersonActedInMoviesConnections match this filter \\"\\"\\" - actedInMoviesConnection_ALL: PersonActedInMoviesConnectionWhere + actedInMoviesConnection_ALL: PersonActedInMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMoviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonActedInMoviesConnections match this filter \\"\\"\\" - actedInMoviesConnection_NONE: PersonActedInMoviesConnectionWhere + actedInMoviesConnection_NONE: PersonActedInMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMoviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonActedInMoviesConnections match this filter \\"\\"\\" - actedInMoviesConnection_SINGLE: PersonActedInMoviesConnectionWhere + actedInMoviesConnection_SINGLE: PersonActedInMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMoviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonActedInMoviesConnections match this filter \\"\\"\\" - actedInMoviesConnection_SOME: PersonActedInMoviesConnectionWhere + actedInMoviesConnection_SOME: PersonActedInMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMoviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related Movies match this filter\\"\\"\\" - actedInMovies_ALL: MovieWhere + actedInMovies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMovies: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related Movies match this filter\\"\\"\\" - actedInMovies_NONE: MovieWhere + actedInMovies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMovies: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related Movies match this filter\\"\\"\\" - actedInMovies_SINGLE: MovieWhere + actedInMovies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMovies: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related Movies match this filter\\"\\"\\" - actedInMovies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedInMovies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInMovies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! } @@ -2304,6 +2652,33 @@ describe("Algebraic", () => { shortest: String } + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/nested-aggregation-on-type.test.ts b/packages/graphql/tests/schema/nested-aggregation-on-type.test.ts index c275371ecd..f751e80d9d 100644 --- a/packages/graphql/tests/schema/nested-aggregation-on-type.test.ts +++ b/packages/graphql/tests/schema/nested-aggregation-on-type.test.ts @@ -42,26 +42,27 @@ describe("nested aggregation on interface", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -73,29 +74,29 @@ describe("nested aggregation on interface", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String! } @@ -103,7 +104,7 @@ describe("nested aggregation on interface", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -115,10 +116,6 @@ describe("nested aggregation on interface", () => { input ActorActedInConnectFieldInput { edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -128,6 +125,25 @@ describe("nested aggregation on interface", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: MovieSort @@ -163,61 +179,64 @@ describe("nested aggregation on interface", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - cost_AVERAGE_EQUAL: Float - cost_AVERAGE_GT: Float - cost_AVERAGE_GTE: Float - cost_AVERAGE_LT: Float - cost_AVERAGE_LTE: Float - cost_MAX_EQUAL: Float - cost_MAX_GT: Float - cost_MAX_GTE: Float - cost_MAX_LT: Float - cost_MAX_LTE: Float - cost_MIN_EQUAL: Float - cost_MIN_GT: Float - cost_MIN_GTE: Float - cost_MIN_LT: Float - cost_MIN_LTE: Float - cost_SUM_EQUAL: Float - cost_SUM_GT: Float - cost_SUM_GTE: Float - cost_SUM_LT: Float - cost_SUM_LTE: Float - runtime_AVERAGE_EQUAL: Float - runtime_AVERAGE_GT: Float - runtime_AVERAGE_GTE: Float - runtime_AVERAGE_LT: Float - runtime_AVERAGE_LTE: Float - runtime_MAX_EQUAL: Int - runtime_MAX_GT: Int - runtime_MAX_GTE: Int - runtime_MAX_LT: Int - runtime_MAX_LTE: Int - runtime_MIN_EQUAL: Int - runtime_MIN_GT: Int - runtime_MIN_GTE: Int - runtime_MIN_LT: Int - runtime_MIN_LTE: Int - runtime_SUM_EQUAL: Int - runtime_SUM_GT: Int - runtime_SUM_GTE: Int - runtime_SUM_LT: Int - runtime_SUM_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + cost: FloatScalarAggregationFilters + cost_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { average: { eq: ... } } }' instead.\\") + cost_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { average: { gt: ... } } }' instead.\\") + cost_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { average: { gte: ... } } }' instead.\\") + cost_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { average: { lt: ... } } }' instead.\\") + cost_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { average: { lte: ... } } }' instead.\\") + cost_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { max: { eq: ... } } }' instead.\\") + cost_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { max: { gt: ... } } }' instead.\\") + cost_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { max: { gte: ... } } }' instead.\\") + cost_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { max: { lt: ... } } }' instead.\\") + cost_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { max: { lte: ... } } }' instead.\\") + cost_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { min: { eq: ... } } }' instead.\\") + cost_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { min: { gt: ... } } }' instead.\\") + cost_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { min: { gte: ... } } }' instead.\\") + cost_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { min: { lt: ... } } }' instead.\\") + cost_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { min: { lte: ... } } }' instead.\\") + cost_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { sum: { eq: ... } } }' instead.\\") + cost_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { sum: { gt: ... } } }' instead.\\") + cost_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { sum: { gte: ... } } }' instead.\\") + cost_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { sum: { lt: ... } } }' instead.\\") + cost_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'cost: { sum: { lte: ... } } }' instead.\\") + runtime: IntScalarAggregationFilters + runtime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { eq: ... } } }' instead.\\") + runtime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gt: ... } } }' instead.\\") + runtime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { gte: ... } } }' instead.\\") + runtime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lt: ... } } }' instead.\\") + runtime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { average: { lte: ... } } }' instead.\\") + runtime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { eq: ... } } }' instead.\\") + runtime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gt: ... } } }' instead.\\") + runtime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { gte: ... } } }' instead.\\") + runtime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lt: ... } } }' instead.\\") + runtime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { max: { lte: ... } } }' instead.\\") + runtime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { eq: ... } } }' instead.\\") + runtime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gt: ... } } }' instead.\\") + runtime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { gte: ... } } }' instead.\\") + runtime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lt: ... } } }' instead.\\") + runtime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { min: { lte: ... } } }' instead.\\") + runtime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { eq: ... } } }' instead.\\") + runtime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gt: ... } } }' instead.\\") + runtime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { gte: ... } } }' instead.\\") + runtime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lt: ... } } }' instead.\\") + runtime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'runtime: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -275,15 +294,6 @@ describe("nested aggregation on interface", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - \\"\\"\\" Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. \\"\\"\\" @@ -293,45 +303,47 @@ describe("nested aggregation on interface", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -373,6 +385,33 @@ describe("nested aggregation on interface", () => { sum: Float } + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + type IntAggregateSelection { average: Float max: Int @@ -380,6 +419,31 @@ describe("nested aggregation on interface", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { cost: Float! runtime: Int! @@ -408,13 +472,15 @@ describe("nested aggregation on interface", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -427,44 +493,44 @@ describe("nested aggregation on interface", () => { } input MovieUpdateInput { - cost: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - cost_ADD: Float - cost_DIVIDE: Float - cost_MULTIPLY: Float - cost_SET: Float - cost_SUBTRACT: Float - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + cost: FloatScalarMutations + cost_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'cost: { add: ... } }' instead.\\") + cost_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'cost: { divide: ... } }' instead.\\") + cost_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'cost: { multiply: ... } }' instead.\\") + cost_SET: Float @deprecated(reason: \\"Please use the generic mutation 'cost: { set: ... } }' instead.\\") + cost_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'cost: { subtract: ... } }' instead.\\") + runtime: IntScalarMutations + runtime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { decrement: ... } }' instead.\\") + runtime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'runtime: { increment: ... } }' instead.\\") + runtime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'runtime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - cost: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - cost_EQ: Float - cost_GT: Float - cost_GTE: Float - cost_IN: [Float!] - cost_LT: Float - cost_LTE: Float - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + cost: FloatScalarFilters + cost_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter cost: { eq: ... }\\") + cost_GT: Float @deprecated(reason: \\"Please use the relevant generic filter cost: { gt: ... }\\") + cost_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter cost: { gte: ... }\\") + cost_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter cost: { in: ... }\\") + cost_LT: Float @deprecated(reason: \\"Please use the relevant generic filter cost: { lt: ... }\\") + cost_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter cost: { lte: ... }\\") + runtime: IntScalarFilters + runtime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { eq: ... }\\") + runtime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gt: ... }\\") + runtime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { gte: ... }\\") + runtime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter runtime: { in: ... }\\") + runtime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lt: ... }\\") + runtime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter runtime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -491,10 +557,10 @@ describe("nested aggregation on interface", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -512,6 +578,27 @@ describe("nested aggregation on interface", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/null.test.ts b/packages/graphql/tests/schema/null.test.ts index ee6bddb691..656a1609e4 100644 --- a/packages/graphql/tests/schema/null.test.ts +++ b/packages/graphql/tests/schema/null.test.ts @@ -50,6 +50,11 @@ describe("Null", () => { mutation: Mutation } + \\"\\"\\"Boolean list filters\\"\\"\\" + input BooleanListFilters { + eq: [Boolean!] + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -71,6 +76,27 @@ describe("Null", () => { min: DateTime } + \\"\\"\\"DateTime list filters\\"\\"\\" + input DateTimeListFilters { + eq: [DateTime!] + includes: DateTime + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -86,9 +112,49 @@ describe("Null", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID list filters\\"\\"\\" + input IDListFilters { + eq: [ID!] + includes: ID + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -98,6 +164,78 @@ describe("Null", () => { sum: Int } + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\"Mutations for a list for Boolean\\"\\"\\" + input ListBooleanMutations { + pop: Int + push: [Boolean!] + set: [Boolean!] + } + + \\"\\"\\"Mutations for a list for DateTime\\"\\"\\" + input ListDateTimeMutations { + pop: Int + push: [DateTime!] + set: [DateTime!] + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + \\"\\"\\"Mutations for a list for ID\\"\\"\\" + input ListIDMutations { + pop: Int + push: [ID!] + set: [ID!] + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] + } + + \\"\\"\\"Mutations for a list for PointInput\\"\\"\\" + input ListPointInputMutations { + pop: Int + push: [PointInput!] + set: [PointInput!] + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + type Movie { actorCount: Int! actorCounts: [Int!]! @@ -119,7 +257,6 @@ describe("Null", () => { averageRating: FloatAggregateSelection! count: Int! createdAt: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") name: StringAggregateSelection! } @@ -144,15 +281,6 @@ describe("Null", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -166,119 +294,119 @@ describe("Null", () => { } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int - actorCounts: [Int!] @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCounts_POP: Int - actorCounts_PUSH: [Int!] - actorCounts_SET: [Int!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - averageRatings: [Float!] @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRatings_POP: Int - averageRatings_PUSH: [Float!] - averageRatings_SET: [Float!] - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAt_SET: DateTime - createdAts: [DateTime!] @deprecated(reason: \\"Please use the explicit _SET field\\") - createdAts_POP: Int - createdAts_PUSH: [DateTime!] - createdAts_SET: [DateTime!] - filmedAt: PointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - filmedAt_SET: PointInput - filmedAts: [PointInput!] @deprecated(reason: \\"Please use the explicit _SET field\\") - filmedAts_POP: Int - filmedAts_PUSH: [PointInput!] - filmedAts_SET: [PointInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - ids: [ID!] @deprecated(reason: \\"Please use the explicit _SET field\\") - ids_POP: Int - ids_PUSH: [ID!] - ids_SET: [ID!] - isActives: [Boolean!] @deprecated(reason: \\"Please use the explicit _SET field\\") - isActives_POP: Int - isActives_PUSH: [Boolean!] - isActives_SET: [Boolean!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - names: [String!] @deprecated(reason: \\"Please use the explicit _SET field\\") - names_POP: Int - names_PUSH: [String!] - names_SET: [String!] + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") + actorCounts: ListIntMutations + actorCounts_POP: Int @deprecated(reason: \\"Please use the generic mutation 'actorCounts: { pop: ... } }' instead.\\") + actorCounts_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'actorCounts: { push: ... } }' instead.\\") + actorCounts_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'actorCounts: { set: ... } }' instead.\\") + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + averageRatings: ListFloatMutations + averageRatings_POP: Int @deprecated(reason: \\"Please use the generic mutation 'averageRatings: { pop: ... } }' instead.\\") + averageRatings_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'averageRatings: { push: ... } }' instead.\\") + averageRatings_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'averageRatings: { set: ... } }' instead.\\") + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + createdAts: ListDateTimeMutations + createdAts_POP: Int @deprecated(reason: \\"Please use the generic mutation 'createdAts: { pop: ... } }' instead.\\") + createdAts_PUSH: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'createdAts: { push: ... } }' instead.\\") + createdAts_SET: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'createdAts: { set: ... } }' instead.\\") + filmedAt: PointMutations + filmedAt_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'filmedAt: { set: ... } }' instead.\\") + filmedAts: ListPointInputMutations + filmedAts_POP: Int @deprecated(reason: \\"Please use the generic mutation 'filmedAts: { pop: ... } }' instead.\\") + filmedAts_PUSH: [PointInput!] @deprecated(reason: \\"Please use the generic mutation 'filmedAts: { push: ... } }' instead.\\") + filmedAts_SET: [PointInput!] @deprecated(reason: \\"Please use the generic mutation 'filmedAts: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + ids: ListIDMutations + ids_POP: Int @deprecated(reason: \\"Please use the generic mutation 'ids: { pop: ... } }' instead.\\") + ids_PUSH: [ID!] @deprecated(reason: \\"Please use the generic mutation 'ids: { push: ... } }' instead.\\") + ids_SET: [ID!] @deprecated(reason: \\"Please use the generic mutation 'ids: { set: ... } }' instead.\\") + isActives: ListBooleanMutations + isActives_POP: Int @deprecated(reason: \\"Please use the generic mutation 'isActives: { pop: ... } }' instead.\\") + isActives_PUSH: [Boolean!] @deprecated(reason: \\"Please use the generic mutation 'isActives: { push: ... } }' instead.\\") + isActives_SET: [Boolean!] @deprecated(reason: \\"Please use the generic mutation 'isActives: { set: ... } }' instead.\\") + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + names: ListStringMutations + names_POP: Int @deprecated(reason: \\"Please use the generic mutation 'names: { pop: ... } }' instead.\\") + names_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'names: { push: ... } }' instead.\\") + names_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'names: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int!] - actorCount_LT: Int - actorCount_LTE: Int - actorCounts: [Int!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCounts_EQ: [Int!] - actorCounts_INCLUDES: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float!] - averageRating_LT: Float - averageRating_LTE: Float - averageRatings: [Float!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRatings_EQ: [Float!] - averageRatings_INCLUDES: Float - createdAt: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAt_EQ: DateTime - createdAt_GT: DateTime - createdAt_GTE: DateTime - createdAt_IN: [DateTime!] - createdAt_LT: DateTime - createdAt_LTE: DateTime - createdAts: [DateTime!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - createdAts_EQ: [DateTime!] - createdAts_INCLUDES: DateTime - filmedAt: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - filmedAt_DISTANCE: PointDistance - filmedAt_EQ: PointInput - filmedAt_GT: PointDistance - filmedAt_GTE: PointDistance - filmedAt_IN: [PointInput!] - filmedAt_LT: PointDistance - filmedAt_LTE: PointDistance - filmedAts: [PointInput!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - filmedAts_EQ: [PointInput!] - filmedAts_INCLUDES: PointInput - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - ids: [ID!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - ids_EQ: [ID!] - ids_INCLUDES: ID - isActives: [Boolean!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActives_EQ: [Boolean!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - names: [String!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - names_EQ: [String!] - names_INCLUDES: String + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actorCounts: IntListFilters + actorCounts_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter actorCounts: { eq: ... }\\") + actorCounts_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter actorCounts: { includes: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + averageRatings: FloatListFilters + averageRatings_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRatings: { eq: ... }\\") + averageRatings_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter averageRatings: { includes: ... }\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + createdAts: DateTimeListFilters + createdAts_EQ: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter createdAts: { eq: ... }\\") + createdAts_INCLUDES: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAts: { includes: ... }\\") + filmedAt: PointFilters + filmedAt_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { distance: ... }\\") + filmedAt_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { eq: ... }\\") + filmedAt_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { gt: ... }\\") + filmedAt_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { gte: ... }\\") + filmedAt_IN: [PointInput!] @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { in: ... }\\") + filmedAt_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { lt: ... }\\") + filmedAt_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { lte: ... }\\") + filmedAts: PointListFilters + filmedAts_EQ: [PointInput!] @deprecated(reason: \\"Please use the relevant generic filter filmedAts: { eq: ... }\\") + filmedAts_INCLUDES: PointInput @deprecated(reason: \\"Please use the relevant generic filter filmedAts: { includes: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + ids: IDListFilters + ids_EQ: [ID!] @deprecated(reason: \\"Please use the relevant generic filter ids: { eq: ... }\\") + ids_INCLUDES: ID @deprecated(reason: \\"Please use the relevant generic filter ids: { includes: ... }\\") + isActives: BooleanListFilters + isActives_EQ: [Boolean!] @deprecated(reason: \\"Please use the relevant generic filter isActives: { eq: ... }\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + names: StringListFilters + names_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter names: { eq: ... }\\") + names_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter names: { includes: ... }\\") } type MoviesConnection { @@ -319,6 +447,23 @@ describe("Null", () => { point: PointInput! } + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + \\"\\"\\"Input type for a point\\"\\"\\" input PointInput { height: Float @@ -326,8 +471,19 @@ describe("Null", () => { longitude: Float! } + \\"\\"\\"Point list filters\\"\\"\\" + input PointListFilters { + eq: [PointInput!] + includes: PointInput + } + + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -345,6 +501,26 @@ describe("Null", () => { shortest: String } + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/pluralize-consistency.test.ts b/packages/graphql/tests/schema/pluralize-consistency.test.ts index 126ced4d44..4b9b8cb4e4 100644 --- a/packages/graphql/tests/schema/pluralize-consistency.test.ts +++ b/packages/graphql/tests/schema/pluralize-consistency.test.ts @@ -69,6 +69,26 @@ describe("Pluralize consistency", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createSuperFriends(input: [super_friendCreateInput!]!): CreateSuperFriendsMutationResponse! createSuperUsers(input: [super_userCreateInput!]!): CreateSuperUsersMutationResponse! @@ -87,10 +107,10 @@ describe("Pluralize consistency", () => { } type Query { - superFriends(limit: Int, offset: Int, options: super_friendOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [super_friendSort!], where: super_friendWhere): [super_friend!]! + superFriends(limit: Int, offset: Int, sort: [super_friendSort!], where: super_friendWhere): [super_friend!]! superFriendsAggregate(where: super_friendWhere): super_friendAggregateSelection! superFriendsConnection(after: String, first: Int, sort: [super_friendSort!], where: super_friendWhere): SuperFriendsConnection! - superUsers(limit: Int, offset: Int, options: super_userOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [super_userSort!], where: super_userWhere): [super_user!]! + superUsers(limit: Int, offset: Int, sort: [super_userSort!], where: super_userWhere): [super_user!]! superUsersAggregate(where: super_userWhere): super_userAggregateSelection! superUsersConnection(after: String, first: Int, sort: [super_userSort!], where: super_userWhere): SuperUsersConnection! } @@ -108,6 +128,27 @@ describe("Pluralize consistency", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type SuperFriendsConnection { edges: [super_friendEdge!]! pageInfo: PageInfo! @@ -162,13 +203,15 @@ describe("Pluralize consistency", () => { node: super_friend! } - input super_friendOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more super_friendSort objects to sort SuperFriends by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [super_friendSort!] + input super_friendRelationshipFilters { + \\"\\"\\"Filter type where all of the related super_friends match this filter\\"\\"\\" + all: super_friendWhere + \\"\\"\\"Filter type where none of the related super_friends match this filter\\"\\"\\" + none: super_friendWhere + \\"\\"\\"Filter type where one of the related super_friends match this filter\\"\\"\\" + single: super_friendWhere + \\"\\"\\"Filter type where some of the related super_friends match this filter\\"\\"\\" + some: super_friendWhere } \\"\\"\\" @@ -179,26 +222,26 @@ describe("Pluralize consistency", () => { } input super_friendUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input super_friendWhere { AND: [super_friendWhere!] NOT: super_friendWhere OR: [super_friendWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type super_user { - my_friend(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: super_friendOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [super_friendSort!], where: super_friendWhere): [super_friend!]! - my_friendAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: super_friendWhere): super_usersuper_friendMy_friendAggregationSelection - my_friendConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [super_userMy_friendConnectionSort!], where: super_userMy_friendConnectionWhere): super_userMy_friendConnection! + my_friend(limit: Int, offset: Int, sort: [super_friendSort!], where: super_friendWhere): [super_friend!]! + my_friendAggregate(where: super_friendWhere): super_usersuper_friendMy_friendAggregationSelection + my_friendConnection(after: String, first: Int, sort: [super_userMy_friendConnectionSort!], where: super_userMy_friendConnectionWhere): super_userMy_friendConnection! name: String! } @@ -225,7 +268,7 @@ describe("Pluralize consistency", () => { AND: [super_userMy_friendAggregateInput!] NOT: super_userMy_friendAggregateInput OR: [super_userMy_friendAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -235,10 +278,6 @@ describe("Pluralize consistency", () => { } input super_userMy_friendConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: super_friendConnectWhere } @@ -248,6 +287,25 @@ describe("Pluralize consistency", () => { totalCount: Int! } + input super_userMy_friendConnectionFilters { + \\"\\"\\" + Return super_users where all of the related super_userMy_friendConnections match this filter + \\"\\"\\" + all: super_userMy_friendConnectionWhere + \\"\\"\\" + Return super_users where none of the related super_userMy_friendConnections match this filter + \\"\\"\\" + none: super_userMy_friendConnectionWhere + \\"\\"\\" + Return super_users where one of the related super_userMy_friendConnections match this filter + \\"\\"\\" + single: super_userMy_friendConnectionWhere + \\"\\"\\" + Return super_users where some of the related super_userMy_friendConnections match this filter + \\"\\"\\" + some: super_userMy_friendConnectionWhere + } + input super_userMy_friendConnectionSort { node: super_friendSort } @@ -280,21 +338,22 @@ describe("Pluralize consistency", () => { AND: [super_userMy_friendNodeAggregationWhereInput!] NOT: super_userMy_friendNodeAggregationWhereInput OR: [super_userMy_friendNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type super_userMy_friendRelationship { @@ -315,15 +374,6 @@ describe("Pluralize consistency", () => { where: super_userMy_friendConnectionWhere } - input super_userOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more super_userSort objects to sort SuperUsers by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [super_userSort!] - } - \\"\\"\\" Fields to sort SuperUsers by. The order in which sorts are applied is not guaranteed when specifying many fields in one super_userSort object. \\"\\"\\" @@ -333,53 +383,55 @@ describe("Pluralize consistency", () => { input super_userUpdateInput { my_friend: [super_userMy_friendUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input super_userWhere { AND: [super_userWhere!] NOT: super_userWhere OR: [super_userWhere!] + my_friend: super_friendRelationshipFilters my_friendAggregate: super_userMy_friendAggregateInput + my_friendConnection: super_userMy_friendConnectionFilters \\"\\"\\" Return super_users where all of the related super_userMy_friendConnections match this filter \\"\\"\\" - my_friendConnection_ALL: super_userMy_friendConnectionWhere + my_friendConnection_ALL: super_userMy_friendConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friendConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return super_users where none of the related super_userMy_friendConnections match this filter \\"\\"\\" - my_friendConnection_NONE: super_userMy_friendConnectionWhere + my_friendConnection_NONE: super_userMy_friendConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friendConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return super_users where one of the related super_userMy_friendConnections match this filter \\"\\"\\" - my_friendConnection_SINGLE: super_userMy_friendConnectionWhere + my_friendConnection_SINGLE: super_userMy_friendConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friendConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return super_users where some of the related super_userMy_friendConnections match this filter \\"\\"\\" - my_friendConnection_SOME: super_userMy_friendConnectionWhere + my_friendConnection_SOME: super_userMy_friendConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friendConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\" Return super_users where all of the related super_friends match this filter \\"\\"\\" - my_friend_ALL: super_friendWhere + my_friend_ALL: super_friendWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friend: { all: ... }' instead.\\") \\"\\"\\" Return super_users where none of the related super_friends match this filter \\"\\"\\" - my_friend_NONE: super_friendWhere + my_friend_NONE: super_friendWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friend: { none: ... }' instead.\\") \\"\\"\\" Return super_users where one of the related super_friends match this filter \\"\\"\\" - my_friend_SINGLE: super_friendWhere + my_friend_SINGLE: super_friendWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friend: { single: ... }' instead.\\") \\"\\"\\" Return super_users where some of the related super_friends match this filter \\"\\"\\" - my_friend_SOME: super_friendWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + my_friend_SOME: super_friendWhere @deprecated(reason: \\"Please use the relevant generic filter 'my_friend: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type super_usersuper_friendMy_friendAggregationSelection { diff --git a/packages/graphql/tests/schema/query-direction.test.ts b/packages/graphql/tests/schema/query-direction.test.ts index 9476ac6eed..8654545fe4 100644 --- a/packages/graphql/tests/schema/query-direction.test.ts +++ b/packages/graphql/tests/schema/query-direction.test.ts @@ -60,6 +60,26 @@ describe("Query Direction", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! @@ -75,7 +95,7 @@ describe("Query Direction", () => { } type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -93,6 +113,27 @@ describe("Query Direction", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -109,7 +150,7 @@ describe("Query Direction", () => { } type User { - friends(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + friends(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! friendsAggregate(where: UserWhere): UserUserFriendsAggregationSelection friendsConnection(after: String, first: Int, sort: [UserFriendsConnectionSort!], where: UserFriendsConnectionWhere): UserFriendsConnection! name: String! @@ -150,7 +191,7 @@ describe("Query Direction", () => { AND: [UserFriendsAggregateInput!] NOT: UserFriendsAggregateInput OR: [UserFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -161,10 +202,6 @@ describe("Query Direction", () => { input UserFriendsConnectFieldInput { connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: UserConnectWhere } @@ -174,6 +211,25 @@ describe("Query Direction", () => { totalCount: Int! } + input UserFriendsConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserFriendsConnections match this filter + \\"\\"\\" + all: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where none of the related UserFriendsConnections match this filter + \\"\\"\\" + none: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where one of the related UserFriendsConnections match this filter + \\"\\"\\" + single: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where some of the related UserFriendsConnections match this filter + \\"\\"\\" + some: UserFriendsConnectionWhere + } + input UserFriendsConnectionSort { node: UserSort } @@ -208,21 +264,22 @@ describe("Query Direction", () => { AND: [UserFriendsNodeAggregationWhereInput!] NOT: UserFriendsNodeAggregationWhereInput OR: [UserFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type UserFriendsRelationship { @@ -243,13 +300,15 @@ describe("Query Direction", () => { where: UserFriendsConnectionWhere } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere } \\"\\"\\" @@ -261,8 +320,8 @@ describe("Query Direction", () => { input UserUpdateInput { friends: [UserFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type UserUserFriendsAggregationSelection { @@ -278,37 +337,39 @@ describe("Query Direction", () => { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] + friends: UserRelationshipFilters friendsAggregate: UserFriendsAggregateInput + friendsConnection: UserFriendsConnectionFilters \\"\\"\\" Return Users where all of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_ALL: UserFriendsConnectionWhere + friendsConnection_ALL: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_NONE: UserFriendsConnectionWhere + friendsConnection_NONE: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_SINGLE: UserFriendsConnectionWhere + friendsConnection_SINGLE: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_SOME: UserFriendsConnectionWhere + friendsConnection_SOME: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - friends_ALL: UserWhere + friends_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - friends_NONE: UserWhere + friends_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - friends_SINGLE: UserWhere + friends_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - friends_SOME: UserWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + friends_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type UsersConnection { @@ -356,6 +417,26 @@ describe("Query Direction", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Mutation { createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! @@ -371,7 +452,7 @@ describe("Query Direction", () => { } type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! usersAggregate(where: UserWhere): UserAggregateSelection! usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! } @@ -389,6 +470,27 @@ describe("Query Direction", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -405,7 +507,7 @@ describe("Query Direction", () => { } type User { - friends(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! + friends(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! friendsAggregate(where: UserWhere): UserUserFriendsAggregationSelection friendsConnection(after: String, first: Int, sort: [UserFriendsConnectionSort!], where: UserFriendsConnectionWhere): UserFriendsConnection! name: String! @@ -446,7 +548,7 @@ describe("Query Direction", () => { AND: [UserFriendsAggregateInput!] NOT: UserFriendsAggregateInput OR: [UserFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -457,10 +559,6 @@ describe("Query Direction", () => { input UserFriendsConnectFieldInput { connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: UserConnectWhere } @@ -470,6 +568,25 @@ describe("Query Direction", () => { totalCount: Int! } + input UserFriendsConnectionFilters { + \\"\\"\\" + Return Users where all of the related UserFriendsConnections match this filter + \\"\\"\\" + all: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where none of the related UserFriendsConnections match this filter + \\"\\"\\" + none: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where one of the related UserFriendsConnections match this filter + \\"\\"\\" + single: UserFriendsConnectionWhere + \\"\\"\\" + Return Users where some of the related UserFriendsConnections match this filter + \\"\\"\\" + some: UserFriendsConnectionWhere + } + input UserFriendsConnectionSort { node: UserSort } @@ -504,21 +621,22 @@ describe("Query Direction", () => { AND: [UserFriendsNodeAggregationWhereInput!] NOT: UserFriendsNodeAggregationWhereInput OR: [UserFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type UserFriendsRelationship { @@ -539,13 +657,15 @@ describe("Query Direction", () => { where: UserFriendsConnectionWhere } - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere } \\"\\"\\" @@ -557,8 +677,8 @@ describe("Query Direction", () => { input UserUpdateInput { friends: [UserFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type UserUserFriendsAggregationSelection { @@ -574,37 +694,39 @@ describe("Query Direction", () => { AND: [UserWhere!] NOT: UserWhere OR: [UserWhere!] + friends: UserRelationshipFilters friendsAggregate: UserFriendsAggregateInput + friendsConnection: UserFriendsConnectionFilters \\"\\"\\" Return Users where all of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_ALL: UserFriendsConnectionWhere + friendsConnection_ALL: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where none of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_NONE: UserFriendsConnectionWhere + friendsConnection_NONE: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where one of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_SINGLE: UserFriendsConnectionWhere + friendsConnection_SINGLE: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Users where some of the related UserFriendsConnections match this filter \\"\\"\\" - friendsConnection_SOME: UserFriendsConnectionWhere + friendsConnection_SOME: UserFriendsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'friendsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - friends_ALL: UserWhere + friends_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { all: ... }' instead.\\") \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - friends_NONE: UserWhere + friends_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { none: ... }' instead.\\") \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - friends_SINGLE: UserWhere + friends_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { single: ... }' instead.\\") \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - friends_SOME: UserWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + friends_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'friends: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type UsersConnection { diff --git a/packages/graphql/tests/schema/remove-deprecated/aggregations-deprecated-disabled.test.ts b/packages/graphql/tests/schema/remove-deprecated/aggregations-deprecated-disabled.test.ts new file mode 100644 index 0000000000..06527b0d99 --- /dev/null +++ b/packages/graphql/tests/schema/remove-deprecated/aggregations-deprecated-disabled.test.ts @@ -0,0 +1,2588 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { gql } from "graphql-tag"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../../src"; + +describe("Deprecated Aggregations disabled", () => { + test("Top Level Aggregations", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + id: ID + isbn: String! + title: String + createdAt: DateTime + someTime: Time + someLocalTime: LocalTime + someLocalDateTime: LocalDateTime + imdbRating: Float + someInt: Int + someBigInt: BigInt + screenTime: Duration + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { excludeDeprecatedFields: { aggregationFilters: true } }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + A BigInt value up to 64 bits in size, which can be a number or a string if used inline, or a string only if used as a variable. Always returned as a string. + \\"\\"\\" + scalar BigInt + + type BigIntAggregateSelection { + average: BigInt + max: BigInt + min: BigInt + sum: BigInt + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Movie { + createdAt: DateTime + id: ID + imdbRating: Float + isbn: String! + screenTime: Duration + someBigInt: BigInt + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + title: String + } + + type MovieAggregateSelection { + count: Int! + createdAt: DateTimeAggregateSelection! + imdbRating: FloatAggregateSelection! + isbn: StringAggregateSelection! + screenTime: DurationAggregateSelection! + someBigInt: BigIntAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someTime: TimeAggregateSelection! + title: StringAggregateSelection! + } + + input MovieCreateInput { + createdAt: DateTime + id: ID + imdbRating: Float + isbn: String! + screenTime: Duration + someBigInt: BigInt + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + title: String + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + createdAt: SortDirection + id: SortDirection + imdbRating: SortDirection + isbn: SortDirection + screenTime: SortDirection + someBigInt: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someTime: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + imdbRating: FloatScalarMutations + imdbRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { add: ... } }' instead.\\") + imdbRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { divide: ... } }' instead.\\") + imdbRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { multiply: ... } }' instead.\\") + imdbRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'imdbRating: { set: ... } }' instead.\\") + imdbRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'imdbRating: { subtract: ... } }' instead.\\") + isbn: StringScalarMutations + isbn_SET: String @deprecated(reason: \\"Please use the generic mutation 'isbn: { set: ... } }' instead.\\") + screenTime: DurationScalarMutations + screenTime_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + imdbRating: FloatScalarFilters + imdbRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { eq: ... }\\") + imdbRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gt: ... }\\") + imdbRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { gte: ... }\\") + imdbRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { in: ... }\\") + imdbRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lt: ... }\\") + imdbRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter imdbRating: { lte: ... }\\") + isbn: StringScalarFilters + isbn_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { contains: ... }\\") + isbn_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { endsWith: ... }\\") + isbn_EQ: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { eq: ... }\\") + isbn_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter isbn: { in: ... }\\") + isbn_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter isbn: { startsWith: ... }\\") + screenTime: DurationScalarFilters + screenTime_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + deleteMovies(where: MovieWhere): DeleteInfo! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Query { + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + }" + `); + }); + + test("Where Level Aggregations", async () => { + const typeDefs = gql` + type User @node { + someId: ID + someString: String + someFloat: Float + someInt: Int + someBigInt: BigInt + someDateTime: DateTime + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + someDuration: Duration + } + + type Post @node { + title: String + likes: [User!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + } + + type Likes @relationshipProperties { + someId: ID + someString: String + someFloat: Float + someInt: Int + someBigInt: BigInt + someDateTime: DateTime + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + someDuration: Duration + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { excludeDeprecatedFields: { aggregationFilters: true } }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + A BigInt value up to 64 bits in size, which can be a number or a string if used inline, or a string only if used as a variable. Always returned as a string. + \\"\\"\\" + scalar BigInt + + type BigIntAggregateSelection { + average: BigInt + max: BigInt + min: BigInt + sum: BigInt + } + + \\"\\"\\"Filters for an aggregation of an BigInt field\\"\\"\\" + input BigIntScalarAggregationFilters { + average: BigIntScalarFilters + max: BigIntScalarFilters + min: BigIntScalarFilters + sum: BigIntScalarFilters + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreatePostsMutationResponse { + info: CreateInfo! + posts: [Post!]! + } + + type CreateUsersMutationResponse { + info: CreateInfo! + users: [User!]! + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\" + The edge properties for the following fields: + * Post.likes + \\"\\"\\" + type Likes { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + input LikesAggregationWhereInput { + AND: [LikesAggregationWhereInput!] + NOT: LikesAggregationWhereInput + OR: [LikesAggregationWhereInput!] + someBigInt: BigIntScalarAggregationFilters + someDateTime: DateTimeScalarAggregationFilters + someDuration: DurationScalarAggregationFilters + someFloat: FloatScalarAggregationFilters + someInt: IntScalarAggregationFilters + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalTime: LocalTimeScalarAggregationFilters + someString: StringScalarAggregationFilters + someTime: TimeScalarAggregationFilters + } + + input LikesCreateInput { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + input LikesSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someId: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input LikesUpdateInput { + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someId: IDScalarMutations + someId_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someId: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input LikesWhere { + AND: [LikesWhere!] + NOT: LikesWhere + OR: [LikesWhere!] + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someId: IDScalarFilters + someId_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { contains: ... }\\") + someId_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { endsWith: ... }\\") + someId_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { eq: ... }\\") + someId_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someId: { in: ... }\\") + someId_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Mutation { + createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! + createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! + deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! + deleteUsers(where: UserWhere): DeleteInfo! + updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! + updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Post { + likes(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + likesAggregate(where: UserWhere): PostUserLikesAggregationSelection + likesConnection(after: String, first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! + title: String + } + + type PostAggregateSelection { + count: Int! + title: StringAggregateSelection! + } + + input PostCreateInput { + likes: PostLikesFieldInput + title: String + } + + input PostDeleteInput { + likes: [PostLikesDeleteFieldInput!] + } + + type PostEdge { + cursor: String! + node: Post! + } + + input PostLikesAggregateInput { + AND: [PostLikesAggregateInput!] + NOT: PostLikesAggregateInput + OR: [PostLikesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: LikesAggregationWhereInput + node: PostLikesNodeAggregationWhereInput + } + + input PostLikesConnectFieldInput { + edge: LikesCreateInput + where: UserConnectWhere + } + + type PostLikesConnection { + edges: [PostLikesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input PostLikesConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + all: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + none: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + single: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + some: PostLikesConnectionWhere + } + + input PostLikesConnectionSort { + edge: LikesSort + node: UserSort + } + + input PostLikesConnectionWhere { + AND: [PostLikesConnectionWhere!] + NOT: PostLikesConnectionWhere + OR: [PostLikesConnectionWhere!] + edge: LikesWhere + node: UserWhere + } + + input PostLikesCreateFieldInput { + edge: LikesCreateInput + node: UserCreateInput! + } + + input PostLikesDeleteFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesDisconnectFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + } + + input PostLikesNodeAggregationWhereInput { + AND: [PostLikesNodeAggregationWhereInput!] + NOT: PostLikesNodeAggregationWhereInput + OR: [PostLikesNodeAggregationWhereInput!] + someBigInt: BigIntScalarAggregationFilters + someDateTime: DateTimeScalarAggregationFilters + someDuration: DurationScalarAggregationFilters + someFloat: FloatScalarAggregationFilters + someInt: IntScalarAggregationFilters + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalTime: LocalTimeScalarAggregationFilters + someString: StringScalarAggregationFilters + someTime: TimeScalarAggregationFilters + } + + type PostLikesRelationship { + cursor: String! + node: User! + properties: Likes! + } + + input PostLikesUpdateConnectionInput { + edge: LikesUpdateInput + node: UserUpdateInput + } + + input PostLikesUpdateFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + delete: [PostLikesDeleteFieldInput!] + disconnect: [PostLikesDisconnectFieldInput!] + update: PostLikesUpdateConnectionInput + where: PostLikesConnectionWhere + } + + \\"\\"\\" + Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. + \\"\\"\\" + input PostSort { + title: SortDirection + } + + input PostUpdateInput { + likes: [PostLikesUpdateFieldInput!] + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + type PostUserLikesAggregationSelection { + count: Int! + edge: PostUserLikesEdgeAggregateSelection + node: PostUserLikesNodeAggregateSelection + } + + type PostUserLikesEdgeAggregateSelection { + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + type PostUserLikesNodeAggregateSelection { + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input PostWhere { + AND: [PostWhere!] + NOT: PostWhere + OR: [PostWhere!] + likes: UserRelationshipFilters + likesAggregate: PostLikesAggregateInput + likesConnection: PostLikesConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_ALL: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_NONE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SINGLE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SOME: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + likes_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + likes_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + likes_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + likes_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type PostsConnection { + edges: [PostEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postsAggregate(where: PostWhere): PostAggregateSelection! + postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + usersAggregate(where: UserWhere): UserAggregateSelection! + usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdatePostsMutationResponse { + info: UpdateInfo! + posts: [Post!]! + } + + type UpdateUsersMutationResponse { + info: UpdateInfo! + users: [User!]! + } + + type User { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserAggregateSelection { + count: Int! + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input UserConnectWhere { + node: UserWhere! + } + + input UserCreateInput { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserEdge { + cursor: String! + node: User! + } + + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere + } + + \\"\\"\\" + Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. + \\"\\"\\" + input UserSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someId: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input UserUpdateInput { + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someId: IDScalarMutations + someId_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someId: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input UserWhere { + AND: [UserWhere!] + NOT: UserWhere + OR: [UserWhere!] + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someId: IDScalarFilters + someId_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { contains: ... }\\") + someId_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { endsWith: ... }\\") + someId_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { eq: ... }\\") + someId_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someId: { in: ... }\\") + someId_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + } + + type UsersConnection { + edges: [UserEdge!]! + pageInfo: PageInfo! + totalCount: Int! + }" + `); + }); + + test("Where Level Aggregations with arrays", async () => { + const typeDefs = gql` + type User @node { + someId: ID + someString: String + someFloat: Float + someInt: Int + someBigInt: BigInt + someDateTime: DateTime + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someTime: Time + someDuration: Duration + } + + type Post @node { + title: String + likes: [User!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") + } + + type Likes @relationshipProperties { + someId: [ID!]! + someString: [String!]! + someFloat: [Float!]! + someInt: [Int!]! + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someTime: [Time!]! + someDuration: [Duration!]! + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { excludeDeprecatedFields: { aggregationFilters: true } }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + A BigInt value up to 64 bits in size, which can be a number or a string if used inline, or a string only if used as a variable. Always returned as a string. + \\"\\"\\" + scalar BigInt + + type BigIntAggregateSelection { + average: BigInt + max: BigInt + min: BigInt + sum: BigInt + } + + \\"\\"\\"BigInt list filters\\"\\"\\" + input BigIntListFilters { + eq: [BigInt!] + includes: BigInt + } + + \\"\\"\\"Filters for an aggregation of an BigInt field\\"\\"\\" + input BigIntScalarAggregationFilters { + average: BigIntScalarFilters + max: BigIntScalarFilters + min: BigIntScalarFilters + sum: BigIntScalarFilters + } + + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreatePostsMutationResponse { + info: CreateInfo! + posts: [Post!]! + } + + type CreateUsersMutationResponse { + info: CreateInfo! + users: [User!]! + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"DateTime list filters\\"\\"\\" + input DateTimeListFilters { + eq: [DateTime!] + includes: DateTime + } + + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Duration list filters\\"\\"\\" + input DurationListFilters { + eq: [Duration!] + includes: Duration + } + + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID list filters\\"\\"\\" + input IDListFilters { + eq: [ID!] + includes: ID + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Int list filters\\"\\"\\" + input IntListFilters { + eq: [Int!] + includes: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\" + The edge properties for the following fields: + * Post.likes + \\"\\"\\" + type Likes { + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someDuration: [Duration!]! + someFloat: [Float!]! + someId: [ID!]! + someInt: [Int!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someString: [String!]! + someTime: [Time!]! + } + + input LikesCreateInput { + someBigInt: [BigInt!]! + someDateTime: [DateTime!]! + someDuration: [Duration!]! + someFloat: [Float!]! + someId: [ID!]! + someInt: [Int!]! + someLocalDateTime: [LocalDateTime!]! + someLocalTime: [LocalTime!]! + someString: [String!]! + someTime: [Time!]! + } + + input LikesSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someId: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input LikesUpdateInput { + someBigInt: ListBigIntMutations + someBigInt_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { pop: ... } }' instead.\\") + someBigInt_PUSH: [BigInt!] @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { push: ... } }' instead.\\") + someBigInt_SET: [BigInt!] @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: ListDateTimeMutations + someDateTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { pop: ... } }' instead.\\") + someDateTime_PUSH: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { push: ... } }' instead.\\") + someDateTime_SET: [DateTime!] @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: ListDurationMutations + someDuration_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someDuration: { pop: ... } }' instead.\\") + someDuration_PUSH: [Duration!] @deprecated(reason: \\"Please use the generic mutation 'someDuration: { push: ... } }' instead.\\") + someDuration_SET: [Duration!] @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: ListFloatMutations + someFloat_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someFloat: { pop: ... } }' instead.\\") + someFloat_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'someFloat: { push: ... } }' instead.\\") + someFloat_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someId: ListIDMutations + someId_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someId: { pop: ... } }' instead.\\") + someId_PUSH: [ID!] @deprecated(reason: \\"Please use the generic mutation 'someId: { push: ... } }' instead.\\") + someId_SET: [ID!] @deprecated(reason: \\"Please use the generic mutation 'someId: { set: ... } }' instead.\\") + someInt: ListIntMutations + someInt_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { pop: ... } }' instead.\\") + someInt_PUSH: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someInt: { push: ... } }' instead.\\") + someInt_SET: [Int!] @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: ListLocalDateTimeMutations + someLocalDateTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { pop: ... } }' instead.\\") + someLocalDateTime_PUSH: [LocalDateTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { push: ... } }' instead.\\") + someLocalDateTime_SET: [LocalDateTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: ListLocalTimeMutations + someLocalTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { pop: ... } }' instead.\\") + someLocalTime_PUSH: [LocalTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { push: ... } }' instead.\\") + someLocalTime_SET: [LocalTime!] @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: ListStringMutations + someString_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someString: { pop: ... } }' instead.\\") + someString_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'someString: { push: ... } }' instead.\\") + someString_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: ListTimeMutations + someTime_POP: Int @deprecated(reason: \\"Please use the generic mutation 'someTime: { pop: ... } }' instead.\\") + someTime_PUSH: [Time!] @deprecated(reason: \\"Please use the generic mutation 'someTime: { push: ... } }' instead.\\") + someTime_SET: [Time!] @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input LikesWhere { + AND: [LikesWhere!] + NOT: LikesWhere + OR: [LikesWhere!] + someBigInt: BigIntListFilters + someBigInt_EQ: [BigInt!] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_INCLUDES: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { includes: ... }\\") + someDateTime: DateTimeListFilters + someDateTime_EQ: [DateTime!] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_INCLUDES: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { includes: ... }\\") + someDuration: DurationListFilters + someDuration_EQ: [Duration!] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_INCLUDES: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { includes: ... }\\") + someFloat: FloatListFilters + someFloat_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { includes: ... }\\") + someId: IDListFilters + someId_EQ: [ID!] @deprecated(reason: \\"Please use the relevant generic filter someId: { eq: ... }\\") + someId_INCLUDES: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { includes: ... }\\") + someInt: IntListFilters + someInt_EQ: [Int!] @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_INCLUDES: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { includes: ... }\\") + someLocalDateTime: LocalDateTimeListFilters + someLocalDateTime_EQ: [LocalDateTime!] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_INCLUDES: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { includes: ... }\\") + someLocalTime: LocalTimeListFilters + someLocalTime_EQ: [LocalTime!] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_INCLUDES: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { includes: ... }\\") + someString: StringListFilters + someString_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter someString: { includes: ... }\\") + someTime: TimeListFilters + someTime_EQ: [Time!] @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_INCLUDES: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { includes: ... }\\") + } + + \\"\\"\\"Mutations for a list for BigInt\\"\\"\\" + input ListBigIntMutations { + pop: Int + push: [BigInt!] + set: [BigInt!] + } + + \\"\\"\\"Mutations for a list for DateTime\\"\\"\\" + input ListDateTimeMutations { + pop: Int + push: [DateTime!] + set: [DateTime!] + } + + \\"\\"\\"Mutations for a list for Duration\\"\\"\\" + input ListDurationMutations { + pop: Int + push: [Duration!] + set: [Duration!] + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + \\"\\"\\"Mutations for a list for ID\\"\\"\\" + input ListIDMutations { + pop: Int + push: [ID!] + set: [ID!] + } + + \\"\\"\\"Mutations for a list for Int\\"\\"\\" + input ListIntMutations { + pop: Int + push: [Int!] + set: [Int!] + } + + \\"\\"\\"Mutations for a list for LocalDateTime\\"\\"\\" + input ListLocalDateTimeMutations { + pop: Int + push: [LocalDateTime!] + set: [LocalDateTime!] + } + + \\"\\"\\"Mutations for a list for LocalTime\\"\\"\\" + input ListLocalTimeMutations { + pop: Int + push: [LocalTime!] + set: [LocalTime!] + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + + \\"\\"\\"Mutations for a list for Time\\"\\"\\" + input ListTimeMutations { + pop: Int + push: [Time!] + set: [Time!] + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"LocalDateTime list filters\\"\\"\\" + input LocalDateTimeListFilters { + eq: [LocalDateTime!] + includes: LocalDateTime + } + + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"LocalTime list filters\\"\\"\\" + input LocalTimeListFilters { + eq: [LocalTime!] + includes: LocalTime + } + + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Mutation { + createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! + createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! + deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! + deleteUsers(where: UserWhere): DeleteInfo! + updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! + updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + type Post { + likes(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + likesAggregate(where: UserWhere): PostUserLikesAggregationSelection + likesConnection(after: String, first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! + title: String + } + + type PostAggregateSelection { + count: Int! + title: StringAggregateSelection! + } + + input PostCreateInput { + likes: PostLikesFieldInput + title: String + } + + input PostDeleteInput { + likes: [PostLikesDeleteFieldInput!] + } + + type PostEdge { + cursor: String! + node: Post! + } + + input PostLikesAggregateInput { + AND: [PostLikesAggregateInput!] + NOT: PostLikesAggregateInput + OR: [PostLikesAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + node: PostLikesNodeAggregationWhereInput + } + + input PostLikesConnectFieldInput { + edge: LikesCreateInput! + where: UserConnectWhere + } + + type PostLikesConnection { + edges: [PostLikesRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input PostLikesConnectionFilters { + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + all: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + none: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + single: PostLikesConnectionWhere + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + some: PostLikesConnectionWhere + } + + input PostLikesConnectionSort { + edge: LikesSort + node: UserSort + } + + input PostLikesConnectionWhere { + AND: [PostLikesConnectionWhere!] + NOT: PostLikesConnectionWhere + OR: [PostLikesConnectionWhere!] + edge: LikesWhere + node: UserWhere + } + + input PostLikesCreateFieldInput { + edge: LikesCreateInput! + node: UserCreateInput! + } + + input PostLikesDeleteFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesDisconnectFieldInput { + where: PostLikesConnectionWhere + } + + input PostLikesFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + } + + input PostLikesNodeAggregationWhereInput { + AND: [PostLikesNodeAggregationWhereInput!] + NOT: PostLikesNodeAggregationWhereInput + OR: [PostLikesNodeAggregationWhereInput!] + someBigInt: BigIntScalarAggregationFilters + someDateTime: DateTimeScalarAggregationFilters + someDuration: DurationScalarAggregationFilters + someFloat: FloatScalarAggregationFilters + someInt: IntScalarAggregationFilters + someLocalDateTime: LocalDateTimeScalarAggregationFilters + someLocalTime: LocalTimeScalarAggregationFilters + someString: StringScalarAggregationFilters + someTime: TimeScalarAggregationFilters + } + + type PostLikesRelationship { + cursor: String! + node: User! + properties: Likes! + } + + input PostLikesUpdateConnectionInput { + edge: LikesUpdateInput + node: UserUpdateInput + } + + input PostLikesUpdateFieldInput { + connect: [PostLikesConnectFieldInput!] + create: [PostLikesCreateFieldInput!] + delete: [PostLikesDeleteFieldInput!] + disconnect: [PostLikesDisconnectFieldInput!] + update: PostLikesUpdateConnectionInput + where: PostLikesConnectionWhere + } + + \\"\\"\\" + Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. + \\"\\"\\" + input PostSort { + title: SortDirection + } + + input PostUpdateInput { + likes: [PostLikesUpdateFieldInput!] + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + + type PostUserLikesAggregationSelection { + count: Int! + node: PostUserLikesNodeAggregateSelection + } + + type PostUserLikesNodeAggregateSelection { + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input PostWhere { + AND: [PostWhere!] + NOT: PostWhere + OR: [PostWhere!] + likes: UserRelationshipFilters + likesAggregate: PostLikesAggregateInput + likesConnection: PostLikesConnectionFilters + \\"\\"\\" + Return Posts where all of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_ALL: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where none of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_NONE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where one of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SINGLE: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Posts where some of the related PostLikesConnections match this filter + \\"\\"\\" + likesConnection_SOME: PostLikesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'likesConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" + likes_ALL: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { all: ... }' instead.\\") + \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" + likes_NONE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { none: ... }' instead.\\") + \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" + likes_SINGLE: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { single: ... }' instead.\\") + \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" + likes_SOME: UserWhere @deprecated(reason: \\"Please use the relevant generic filter 'likes: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") + } + + type PostsConnection { + edges: [PostEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Query { + posts(limit: Int, offset: Int, sort: [PostSort!], where: PostWhere): [Post!]! + postsAggregate(where: PostWhere): PostAggregateSelection! + postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! + users(limit: Int, offset: Int, sort: [UserSort!], where: UserWhere): [User!]! + usersAggregate(where: UserWhere): UserAggregateSelection! + usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Time list filters\\"\\"\\" + input TimeListFilters { + eq: [Time!] + includes: Time + } + + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdatePostsMutationResponse { + info: UpdateInfo! + posts: [Post!]! + } + + type UpdateUsersMutationResponse { + info: UpdateInfo! + users: [User!]! + } + + type User { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserAggregateSelection { + count: Int! + someBigInt: BigIntAggregateSelection! + someDateTime: DateTimeAggregateSelection! + someDuration: DurationAggregateSelection! + someFloat: FloatAggregateSelection! + someInt: IntAggregateSelection! + someLocalDateTime: LocalDateTimeAggregateSelection! + someLocalTime: LocalTimeAggregateSelection! + someString: StringAggregateSelection! + someTime: TimeAggregateSelection! + } + + input UserConnectWhere { + node: UserWhere! + } + + input UserCreateInput { + someBigInt: BigInt + someDateTime: DateTime + someDuration: Duration + someFloat: Float + someId: ID + someInt: Int + someLocalDateTime: LocalDateTime + someLocalTime: LocalTime + someString: String + someTime: Time + } + + type UserEdge { + cursor: String! + node: User! + } + + input UserRelationshipFilters { + \\"\\"\\"Filter type where all of the related Users match this filter\\"\\"\\" + all: UserWhere + \\"\\"\\"Filter type where none of the related Users match this filter\\"\\"\\" + none: UserWhere + \\"\\"\\"Filter type where one of the related Users match this filter\\"\\"\\" + single: UserWhere + \\"\\"\\"Filter type where some of the related Users match this filter\\"\\"\\" + some: UserWhere + } + + \\"\\"\\" + Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. + \\"\\"\\" + input UserSort { + someBigInt: SortDirection + someDateTime: SortDirection + someDuration: SortDirection + someFloat: SortDirection + someId: SortDirection + someInt: SortDirection + someLocalDateTime: SortDirection + someLocalTime: SortDirection + someString: SortDirection + someTime: SortDirection + } + + input UserUpdateInput { + someBigInt: BigIntScalarMutations + someBigInt_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { decrement: ... } }' instead.\\") + someBigInt_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'someBigInt: { increment: ... } }' instead.\\") + someBigInt_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'someBigInt: { set: ... } }' instead.\\") + someDateTime: DateTimeScalarMutations + someDateTime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'someDateTime: { set: ... } }' instead.\\") + someDuration: DurationScalarMutations + someDuration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'someDuration: { set: ... } }' instead.\\") + someFloat: FloatScalarMutations + someFloat_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { add: ... } }' instead.\\") + someFloat_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { divide: ... } }' instead.\\") + someFloat_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { multiply: ... } }' instead.\\") + someFloat_SET: Float @deprecated(reason: \\"Please use the generic mutation 'someFloat: { set: ... } }' instead.\\") + someFloat_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'someFloat: { subtract: ... } }' instead.\\") + someId: IDScalarMutations + someId_SET: ID @deprecated(reason: \\"Please use the generic mutation 'someId: { set: ... } }' instead.\\") + someInt: IntScalarMutations + someInt_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { decrement: ... } }' instead.\\") + someInt_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'someInt: { increment: ... } }' instead.\\") + someInt_SET: Int @deprecated(reason: \\"Please use the generic mutation 'someInt: { set: ... } }' instead.\\") + someLocalDateTime: LocalDateTimeScalarMutations + someLocalDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'someLocalDateTime: { set: ... } }' instead.\\") + someLocalTime: LocalTimeScalarMutations + someLocalTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'someLocalTime: { set: ... } }' instead.\\") + someString: StringScalarMutations + someString_SET: String @deprecated(reason: \\"Please use the generic mutation 'someString: { set: ... } }' instead.\\") + someTime: TimeScalarMutations + someTime_SET: Time @deprecated(reason: \\"Please use the generic mutation 'someTime: { set: ... } }' instead.\\") + } + + input UserWhere { + AND: [UserWhere!] + NOT: UserWhere + OR: [UserWhere!] + someBigInt: BigIntScalarFilters + someBigInt_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { eq: ... }\\") + someBigInt_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gt: ... }\\") + someBigInt_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { gte: ... }\\") + someBigInt_IN: [BigInt] @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { in: ... }\\") + someBigInt_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lt: ... }\\") + someBigInt_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter someBigInt: { lte: ... }\\") + someDateTime: DateTimeScalarFilters + someDateTime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { eq: ... }\\") + someDateTime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gt: ... }\\") + someDateTime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { gte: ... }\\") + someDateTime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { in: ... }\\") + someDateTime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lt: ... }\\") + someDateTime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter someDateTime: { lte: ... }\\") + someDuration: DurationScalarFilters + someDuration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { eq: ... }\\") + someDuration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gt: ... }\\") + someDuration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { gte: ... }\\") + someDuration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter someDuration: { in: ... }\\") + someDuration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lt: ... }\\") + someDuration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter someDuration: { lte: ... }\\") + someFloat: FloatScalarFilters + someFloat_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { eq: ... }\\") + someFloat_GT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gt: ... }\\") + someFloat_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { gte: ... }\\") + someFloat_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter someFloat: { in: ... }\\") + someFloat_LT: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lt: ... }\\") + someFloat_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter someFloat: { lte: ... }\\") + someId: IDScalarFilters + someId_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { contains: ... }\\") + someId_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { endsWith: ... }\\") + someId_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { eq: ... }\\") + someId_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter someId: { in: ... }\\") + someId_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter someId: { startsWith: ... }\\") + someInt: IntScalarFilters + someInt_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { eq: ... }\\") + someInt_GT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gt: ... }\\") + someInt_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { gte: ... }\\") + someInt_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter someInt: { in: ... }\\") + someInt_LT: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lt: ... }\\") + someInt_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter someInt: { lte: ... }\\") + someLocalDateTime: LocalDateTimeScalarFilters + someLocalDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { eq: ... }\\") + someLocalDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gt: ... }\\") + someLocalDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { gte: ... }\\") + someLocalDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { in: ... }\\") + someLocalDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lt: ... }\\") + someLocalDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter someLocalDateTime: { lte: ... }\\") + someLocalTime: LocalTimeScalarFilters + someLocalTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { eq: ... }\\") + someLocalTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gt: ... }\\") + someLocalTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { gte: ... }\\") + someLocalTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { in: ... }\\") + someLocalTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lt: ... }\\") + someLocalTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter someLocalTime: { lte: ... }\\") + someString: StringScalarFilters + someString_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter someString: { contains: ... }\\") + someString_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { endsWith: ... }\\") + someString_EQ: String @deprecated(reason: \\"Please use the relevant generic filter someString: { eq: ... }\\") + someString_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter someString: { in: ... }\\") + someString_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter someString: { startsWith: ... }\\") + someTime: TimeScalarFilters + someTime_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { eq: ... }\\") + someTime_GT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gt: ... }\\") + someTime_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { gte: ... }\\") + someTime_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter someTime: { in: ... }\\") + someTime_LT: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lt: ... }\\") + someTime_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter someTime: { lte: ... }\\") + } + + type UsersConnection { + edges: [UserEdge!]! + pageInfo: PageInfo! + totalCount: Int! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/remove-deprecated/aggregations.test.ts b/packages/graphql/tests/schema/remove-deprecated/aggregations.test.ts deleted file mode 100644 index 5a681f045d..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/aggregations.test.ts +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Aggregations", () => { - test("should remove ID Aggregations", async () => { - const typeDefs = /* GraphQL */ ` - type User @node { - someID: ID - someString: String - } - - type Post @node { - someID: ID - title: String - likes: [User!]! @relationship(type: "LIKES", direction: IN, properties: "Likes") - } - - type Likes @relationshipProperties { - someID: ID - someString: String - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { excludeDeprecatedFields: { idAggregations: true } }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreatePostsMutationResponse { - info: CreateInfo! - posts: [Post!]! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - \\"\\"\\" - The edge properties for the following fields: - * Post.likes - \\"\\"\\" - type Likes { - someID: ID - someString: String - } - - input LikesAggregationWhereInput { - AND: [LikesAggregationWhereInput!] - NOT: LikesAggregationWhereInput - OR: [LikesAggregationWhereInput!] - someString_AVERAGE_LENGTH_EQUAL: Float - someString_AVERAGE_LENGTH_GT: Float - someString_AVERAGE_LENGTH_GTE: Float - someString_AVERAGE_LENGTH_LT: Float - someString_AVERAGE_LENGTH_LTE: Float - someString_LONGEST_LENGTH_EQUAL: Int - someString_LONGEST_LENGTH_GT: Int - someString_LONGEST_LENGTH_GTE: Int - someString_LONGEST_LENGTH_LT: Int - someString_LONGEST_LENGTH_LTE: Int - someString_SHORTEST_LENGTH_EQUAL: Int - someString_SHORTEST_LENGTH_GT: Int - someString_SHORTEST_LENGTH_GTE: Int - someString_SHORTEST_LENGTH_LT: Int - someString_SHORTEST_LENGTH_LTE: Int - } - - input LikesCreateInput { - someID: ID - someString: String - } - - input LikesSort { - someID: SortDirection - someString: SortDirection - } - - input LikesUpdateInput { - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - someString: String @deprecated(reason: \\"Please use the explicit _SET field\\") - someString_SET: String - } - - input LikesWhere { - AND: [LikesWhere!] - NOT: LikesWhere - OR: [LikesWhere!] - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - someString: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - someString_CONTAINS: String - someString_ENDS_WITH: String - someString_EQ: String - someString_IN: [String] - someString_STARTS_WITH: String - } - - type Mutation { - createPosts(input: [PostCreateInput!]!): CreatePostsMutationResponse! - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deletePosts(delete: PostDeleteInput, where: PostWhere): DeleteInfo! - deleteUsers(where: UserWhere): DeleteInfo! - updatePosts(update: PostUpdateInput, where: PostWhere): UpdatePostsMutationResponse! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Post { - likes(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - likesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): PostUserLikesAggregationSelection - likesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PostLikesConnectionSort!], where: PostLikesConnectionWhere): PostLikesConnection! - someID: ID - title: String - } - - type PostAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - input PostCreateInput { - likes: PostLikesFieldInput - someID: ID - title: String - } - - input PostDeleteInput { - likes: [PostLikesDeleteFieldInput!] - } - - type PostEdge { - cursor: String! - node: Post! - } - - input PostLikesAggregateInput { - AND: [PostLikesAggregateInput!] - NOT: PostLikesAggregateInput - OR: [PostLikesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: LikesAggregationWhereInput - node: PostLikesNodeAggregationWhereInput - } - - input PostLikesConnectFieldInput { - edge: LikesCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type PostLikesConnection { - edges: [PostLikesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input PostLikesConnectionSort { - edge: LikesSort - node: UserSort - } - - input PostLikesConnectionWhere { - AND: [PostLikesConnectionWhere!] - NOT: PostLikesConnectionWhere - OR: [PostLikesConnectionWhere!] - edge: LikesWhere - node: UserWhere - } - - input PostLikesCreateFieldInput { - edge: LikesCreateInput - node: UserCreateInput! - } - - input PostLikesDeleteFieldInput { - where: PostLikesConnectionWhere - } - - input PostLikesDisconnectFieldInput { - where: PostLikesConnectionWhere - } - - input PostLikesFieldInput { - connect: [PostLikesConnectFieldInput!] - create: [PostLikesCreateFieldInput!] - } - - input PostLikesNodeAggregationWhereInput { - AND: [PostLikesNodeAggregationWhereInput!] - NOT: PostLikesNodeAggregationWhereInput - OR: [PostLikesNodeAggregationWhereInput!] - someString_AVERAGE_LENGTH_EQUAL: Float - someString_AVERAGE_LENGTH_GT: Float - someString_AVERAGE_LENGTH_GTE: Float - someString_AVERAGE_LENGTH_LT: Float - someString_AVERAGE_LENGTH_LTE: Float - someString_LONGEST_LENGTH_EQUAL: Int - someString_LONGEST_LENGTH_GT: Int - someString_LONGEST_LENGTH_GTE: Int - someString_LONGEST_LENGTH_LT: Int - someString_LONGEST_LENGTH_LTE: Int - someString_SHORTEST_LENGTH_EQUAL: Int - someString_SHORTEST_LENGTH_GT: Int - someString_SHORTEST_LENGTH_GTE: Int - someString_SHORTEST_LENGTH_LT: Int - someString_SHORTEST_LENGTH_LTE: Int - } - - type PostLikesRelationship { - cursor: String! - node: User! - properties: Likes! - } - - input PostLikesUpdateConnectionInput { - edge: LikesUpdateInput - node: UserUpdateInput - } - - input PostLikesUpdateFieldInput { - connect: [PostLikesConnectFieldInput!] - create: [PostLikesCreateFieldInput!] - delete: [PostLikesDeleteFieldInput!] - disconnect: [PostLikesDisconnectFieldInput!] - update: PostLikesUpdateConnectionInput - where: PostLikesConnectionWhere - } - - input PostOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PostSort objects to sort Posts by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PostSort!] - } - - \\"\\"\\" - Fields to sort Posts by. The order in which sorts are applied is not guaranteed when specifying many fields in one PostSort object. - \\"\\"\\" - input PostSort { - someID: SortDirection - title: SortDirection - } - - input PostUpdateInput { - likes: [PostLikesUpdateFieldInput!] - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - type PostUserLikesAggregationSelection { - count: Int! - edge: PostUserLikesEdgeAggregateSelection - node: PostUserLikesNodeAggregateSelection - } - - type PostUserLikesEdgeAggregateSelection { - someString: StringAggregateSelection! - } - - type PostUserLikesNodeAggregateSelection { - someString: StringAggregateSelection! - } - - input PostWhere { - AND: [PostWhere!] - NOT: PostWhere - OR: [PostWhere!] - likesAggregate: PostLikesAggregateInput - \\"\\"\\" - Return Posts where all of the related PostLikesConnections match this filter - \\"\\"\\" - likesConnection_ALL: PostLikesConnectionWhere - \\"\\"\\" - Return Posts where none of the related PostLikesConnections match this filter - \\"\\"\\" - likesConnection_NONE: PostLikesConnectionWhere - \\"\\"\\" - Return Posts where one of the related PostLikesConnections match this filter - \\"\\"\\" - likesConnection_SINGLE: PostLikesConnectionWhere - \\"\\"\\" - Return Posts where some of the related PostLikesConnections match this filter - \\"\\"\\" - likesConnection_SOME: PostLikesConnectionWhere - \\"\\"\\"Return Posts where all of the related Users match this filter\\"\\"\\" - likes_ALL: UserWhere - \\"\\"\\"Return Posts where none of the related Users match this filter\\"\\"\\" - likes_NONE: UserWhere - \\"\\"\\"Return Posts where one of the related Users match this filter\\"\\"\\" - likes_SINGLE: UserWhere - \\"\\"\\"Return Posts where some of the related Users match this filter\\"\\"\\" - likes_SOME: UserWhere - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String - } - - type PostsConnection { - edges: [PostEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - posts(limit: Int, offset: Int, options: PostOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PostSort!], where: PostWhere): [Post!]! - postsAggregate(where: PostWhere): PostAggregateSelection! - postsConnection(after: String, first: Int, sort: [PostSort!], where: PostWhere): PostsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdatePostsMutationResponse { - info: UpdateInfo! - posts: [Post!]! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - someID: ID - someString: String - } - - type UserAggregateSelection { - count: Int! - someString: StringAggregateSelection! - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - someID: ID - someString: String - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - someID: SortDirection - someString: SortDirection - } - - input UserUpdateInput { - someID: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - someID_SET: ID - someString: String @deprecated(reason: \\"Please use the explicit _SET field\\") - someString_SET: String - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - someID: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - someID_CONTAINS: ID - someID_ENDS_WITH: ID - someID_EQ: ID - someID_IN: [ID] - someID_STARTS_WITH: ID - someString: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - someString_CONTAINS: String - someString_ENDS_WITH: String - someString_EQ: String - someString_IN: [String] - someString_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts b/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts deleted file mode 100644 index 8918610362..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/array-methods.test.ts +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Arrays Methods", () => { - test("Array of Float and Array of Actor relationships", async () => { - const typeDefs = gql` - type Actor @node { - name: String - actedIn: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type Movie @node { - id: ID! - ratings: [Float!]! - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - averageRating: Float! - } - - type ActedIn @relationshipProperties { - pay: [Float] - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.actedIn - * Movie.actors - \\"\\"\\" - type ActedIn { - pay: [Float] - } - - input ActedInCreateInput { - pay: [Float] - } - - input ActedInSort { - pay: SortDirection - } - - input ActedInUpdateInput { - pay: [Float] @deprecated(reason: \\"Please use the explicit _SET field\\") - pay_POP: Int - pay_PUSH: [Float] - pay_SET: [Float] - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - pay: [Float] @deprecated(reason: \\"Please use the explicit _EQ version\\") - pay_EQ: [Float] - pay_INCLUDES: Float - } - - type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! - name: String - } - - input ActorActedInAggregateInput { - AND: [ActorActedInAggregateInput!] - NOT: ActorActedInAggregateInput - OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorActedInNodeAggregationWhereInput - } - - input ActorActedInConnectFieldInput { - connect: [MovieConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorActedInConnection { - edges: [ActorActedInRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorActedInConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorActedInConnectionWhere { - AND: [ActorActedInConnectionWhere!] - NOT: ActorActedInConnectionWhere - OR: [ActorActedInConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorActedInCreateFieldInput { - edge: ActedInCreateInput - node: MovieCreateInput! - } - - input ActorActedInDeleteFieldInput { - delete: MovieDeleteInput - where: ActorActedInConnectionWhere - } - - input ActorActedInDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: ActorActedInConnectionWhere - } - - input ActorActedInFieldInput { - connect: [ActorActedInConnectFieldInput!] - create: [ActorActedInCreateFieldInput!] - } - - input ActorActedInNodeAggregationWhereInput { - AND: [ActorActedInNodeAggregationWhereInput!] - NOT: ActorActedInNodeAggregationWhereInput - OR: [ActorActedInNodeAggregationWhereInput!] - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type ActorActedInRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorActedInUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorActedInUpdateFieldInput { - connect: [ActorActedInConnectFieldInput!] - create: [ActorActedInCreateFieldInput!] - delete: [ActorActedInDeleteFieldInput!] - disconnect: [ActorActedInDisconnectFieldInput!] - update: ActorActedInUpdateConnectionInput - where: ActorActedInConnectionWhere - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectInput { - actedIn: [ActorActedInConnectFieldInput!] - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - actedIn: ActorActedInFieldInput - name: String - } - - input ActorDeleteInput { - actedIn: [ActorActedInDeleteFieldInput!] - } - - input ActorDisconnectInput { - actedIn: [ActorActedInDisconnectFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieActedInAggregationSelection { - count: Int! - node: ActorMovieActedInNodeAggregateSelection - } - - type ActorMovieActedInNodeAggregateSelection { - averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - actedInAggregate: ActorActedInAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type FloatAggregateSelection { - average: Float - max: Float - min: Float - sum: Float - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - averageRating: Float! - id: ID! - ratings: [Float!]! - } - - type MovieActorActorsAggregationSelection { - count: Int! - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - connect: [ActorConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - edge: ActedInSort - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - edge: ActedInWhere - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - edge: ActedInCreateInput - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - delete: ActorDeleteInput - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - disconnect: ActorDisconnectInput - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - properties: ActedIn! - } - - input MovieActorsUpdateConnectionInput { - edge: ActedInUpdateInput - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - averageRating: FloatAggregateSelection! - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieConnectInput { - actors: [MovieActorsConnectFieldInput!] - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - averageRating: Float! - id: ID! - ratings: [Float!]! - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - input MovieDisconnectInput { - actors: [MovieActorsDisconnectFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - averageRating: SortDirection - id: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _SET field\\") - ratings_POP: Int - ratings_PUSH: [Float!] - ratings_SET: [Float!] - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float!] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - ratings: [Float!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - ratings_EQ: [Float!] - ratings_INCLUDES: Float - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/attribute-filtering.test.ts b/packages/graphql/tests/schema/remove-deprecated/attribute-filtering.test.ts new file mode 100644 index 0000000000..9be5489abb --- /dev/null +++ b/packages/graphql/tests/schema/remove-deprecated/attribute-filtering.test.ts @@ -0,0 +1,1413 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../../src"; + +describe("Exclude attribute suffix based filtering", () => { + test("should exclude attribute suffix based filtering", async () => { + const typeDefs = /* GraphQL */ ` + type typeA @node { + name: String + actedIn: [typeB!]! @relationship(type: "HAS_TYPE", properties: "relType", direction: OUT) + } + + type typeB implements interfaceC @node { + id: ID! + ratings: [Float!]! + rels: [typeA!]! @relationship(type: "HAS_TYPE", properties: "relType", direction: IN) + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + list: [String!]! + } + union d = typeA | typeB + + interface interfaceC { + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + list: [String!]! + } + + type relType @relationshipProperties { + pay: [Float!] + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { + excludeDeprecatedFields: { + attributeFilters: true, + }, + }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\"Distance filters for cartesian points\\"\\"\\" + input CartesianDistancePointFilters { + from: CartesianPointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\" + A point in a two- or three-dimensional Cartesian coordinate system or in a three-dimensional cylindrical coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#cartesian-point + \\"\\"\\" + type CartesianPoint { + crs: String! + srid: Int! + x: Float! + y: Float! + z: Float + } + + \\"\\"\\"Cartesian Point filters\\"\\"\\" + input CartesianPointFilters { + distance: CartesianDistancePointFilters + eq: CartesianPointInput + in: [CartesianPointInput!] + } + + \\"\\"\\"Input type for a cartesian point\\"\\"\\" + input CartesianPointInput { + x: Float! + y: Float! + z: Float + } + + \\"\\"\\"CartesianPoint mutations\\"\\"\\" + input CartesianPointMutations { + set: CartesianPointInput + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateTypeASMutationResponse { + info: CreateInfo! + typeAS: [typeA!]! + } + + type CreateTypeBSMutationResponse { + info: CreateInfo! + typeBS: [typeB!]! + } + + \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" + scalar Date + + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + + \\"\\"\\"Date mutations\\"\\"\\" + input DateScalarMutations { + set: Date + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + type InterfaceCSConnection { + edges: [interfaceCEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Mutation { + createTypeAS(input: [typeACreateInput!]!): CreateTypeASMutationResponse! + createTypeBS(input: [typeBCreateInput!]!): CreateTypeBSMutationResponse! + deleteTypeAS(delete: typeADeleteInput, where: typeAWhere): DeleteInfo! + deleteTypeBS(delete: typeBDeleteInput, where: typeBWhere): DeleteInfo! + updateTypeAS(update: typeAUpdateInput, where: typeAWhere): UpdateTypeASMutationResponse! + updateTypeBS(update: typeBUpdateInput, where: typeBWhere): UpdateTypeBSMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + \\"\\"\\" + A point in a coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#point + \\"\\"\\" + type Point { + crs: String! + height: Float + latitude: Float! + longitude: Float! + srid: Int! + } + + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + + \\"\\"\\"Input type for a point\\"\\"\\" + input PointInput { + height: Float + latitude: Float! + longitude: Float! + } + + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + + type Query { + ds(limit: Int, offset: Int, where: dWhere): [d!]! + interfaceCS(limit: Int, offset: Int, sort: [interfaceCSort!], where: interfaceCWhere): [interfaceC!]! + interfaceCSAggregate(where: interfaceCWhere): interfaceCAggregateSelection! + interfaceCSConnection(after: String, first: Int, sort: [interfaceCSort!], where: interfaceCWhere): InterfaceCSConnection! + typeAS(limit: Int, offset: Int, sort: [typeASort!], where: typeAWhere): [typeA!]! + typeASAggregate(where: typeAWhere): typeAAggregateSelection! + typeASConnection(after: String, first: Int, sort: [typeASort!], where: typeAWhere): TypeASConnection! + typeBS(limit: Int, offset: Int, sort: [typeBSort!], where: typeBWhere): [typeB!]! + typeBSAggregate(where: typeBWhere): typeBAggregateSelection! + typeBSConnection(after: String, first: Int, sort: [typeBSort!], where: typeBWhere): TypeBSConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + type TypeASConnection { + edges: [typeAEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type TypeBSConnection { + edges: [typeBEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateTypeASMutationResponse { + info: UpdateInfo! + typeAS: [typeA!]! + } + + type UpdateTypeBSMutationResponse { + info: UpdateInfo! + typeBS: [typeB!]! + } + + union d = typeA | typeB + + input dWhere { + typeA: typeAWhere + typeB: typeBWhere + } + + interface interfaceC { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: Point + time: Time + } + + type interfaceCAggregateSelection { + averageRating: FloatAggregateSelection! + count: Int! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type interfaceCEdge { + cursor: String! + node: interfaceC! + } + + enum interfaceCImplementation { + typeB + } + + \\"\\"\\" + Fields to sort InterfaceCS by. The order in which sorts are applied is not guaranteed when specifying many fields in one interfaceCSort object. + \\"\\"\\" + input interfaceCSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + localDateTime: SortDirection + localTime: SortDirection + point: SortDirection + time: SortDirection + } + + input interfaceCWhere { + AND: [interfaceCWhere!] + NOT: interfaceCWhere + OR: [interfaceCWhere!] + averageRating: FloatScalarFilters + cartesianPoint: CartesianPointFilters + createdAt: DateTimeScalarFilters + date: DateScalarFilters + duration: DurationScalarFilters + list: StringListFilters + localDateTime: LocalDateTimeScalarFilters + localTime: LocalTimeScalarFilters + point: PointFilters + time: TimeScalarFilters + typename: [interfaceCImplementation!] + } + + \\"\\"\\" + The edge properties for the following fields: + * typeA.actedIn + * typeB.rels + \\"\\"\\" + type relType { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + localDateTime: LocalDateTime + localTime: LocalTime + pay: [Float!] + point: Point + time: Time + } + + input relTypeAggregationWhereInput { + AND: [relTypeAggregationWhereInput!] + NOT: relTypeAggregationWhereInput + OR: [relTypeAggregationWhereInput!] + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + duration: DurationScalarAggregationFilters + duration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { eq: ... } } }' instead.\\") + duration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gt: ... } } }' instead.\\") + duration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gte: ... } } }' instead.\\") + duration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lt: ... } } }' instead.\\") + duration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lte: ... } } }' instead.\\") + duration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { eq: ... } } }' instead.\\") + duration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gt: ... } } }' instead.\\") + duration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gte: ... } } }' instead.\\") + duration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lt: ... } } }' instead.\\") + duration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lte: ... } } }' instead.\\") + duration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { eq: ... } } }' instead.\\") + duration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gt: ... } } }' instead.\\") + duration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gte: ... } } }' instead.\\") + duration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lt: ... } } }' instead.\\") + duration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lte: ... } } }' instead.\\") + localDateTime: LocalDateTimeScalarAggregationFilters + localDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { eq: ... } } }' instead.\\") + localDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gt: ... } } }' instead.\\") + localDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gte: ... } } }' instead.\\") + localDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lt: ... } } }' instead.\\") + localDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lte: ... } } }' instead.\\") + localDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { eq: ... } } }' instead.\\") + localDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gt: ... } } }' instead.\\") + localDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gte: ... } } }' instead.\\") + localDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lt: ... } } }' instead.\\") + localDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lte: ... } } }' instead.\\") + localTime: LocalTimeScalarAggregationFilters + localTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { eq: ... } } }' instead.\\") + localTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gt: ... } } }' instead.\\") + localTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gte: ... } } }' instead.\\") + localTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lt: ... } } }' instead.\\") + localTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lte: ... } } }' instead.\\") + localTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { eq: ... } } }' instead.\\") + localTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gt: ... } } }' instead.\\") + localTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gte: ... } } }' instead.\\") + localTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lt: ... } } }' instead.\\") + localTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lte: ... } } }' instead.\\") + time: TimeScalarAggregationFilters + time_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { eq: ... } } }' instead.\\") + time_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gt: ... } } }' instead.\\") + time_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gte: ... } } }' instead.\\") + time_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lt: ... } } }' instead.\\") + time_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lte: ... } } }' instead.\\") + time_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { eq: ... } } }' instead.\\") + time_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gt: ... } } }' instead.\\") + time_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gte: ... } } }' instead.\\") + time_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lt: ... } } }' instead.\\") + time_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lte: ... } } }' instead.\\") + } + + input relTypeCreateInput { + averageRating: Float! + cartesianPoint: CartesianPointInput + createdAt: DateTime + date: Date + duration: Duration + localDateTime: LocalDateTime + localTime: LocalTime + pay: [Float!] + point: PointInput + time: Time + } + + input relTypeSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + localDateTime: SortDirection + localTime: SortDirection + pay: SortDirection + point: SortDirection + time: SortDirection + } + + input relTypeUpdateInput { + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + cartesianPoint: CartesianPointMutations + cartesianPoint_SET: CartesianPointInput @deprecated(reason: \\"Please use the generic mutation 'cartesianPoint: { set: ... } }' instead.\\") + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + date: DateScalarMutations + date_SET: Date @deprecated(reason: \\"Please use the generic mutation 'date: { set: ... } }' instead.\\") + duration: DurationScalarMutations + duration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'duration: { set: ... } }' instead.\\") + localDateTime: LocalDateTimeScalarMutations + localDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'localDateTime: { set: ... } }' instead.\\") + localTime: LocalTimeScalarMutations + localTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'localTime: { set: ... } }' instead.\\") + pay: ListFloatMutations + pay_POP: Int @deprecated(reason: \\"Please use the generic mutation 'pay: { pop: ... } }' instead.\\") + pay_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { push: ... } }' instead.\\") + pay_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { set: ... } }' instead.\\") + point: PointMutations + point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'point: { set: ... } }' instead.\\") + time: TimeScalarMutations + time_SET: Time @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") + } + + input relTypeWhere { + AND: [relTypeWhere!] + NOT: relTypeWhere + OR: [relTypeWhere!] + averageRating: FloatScalarFilters + cartesianPoint: CartesianPointFilters + createdAt: DateTimeScalarFilters + date: DateScalarFilters + duration: DurationScalarFilters + localDateTime: LocalDateTimeScalarFilters + localTime: LocalTimeScalarFilters + pay: FloatListFilters + point: PointFilters + time: TimeScalarFilters + } + + type typeA { + actedIn(limit: Int, offset: Int, sort: [typeBSort!], where: typeBWhere): [typeB!]! + actedInAggregate(where: typeBWhere): typeAtypeBActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [typeAActedInConnectionSort!], where: typeAActedInConnectionWhere): typeAActedInConnection! + name: String + } + + input typeAActedInAggregateInput { + AND: [typeAActedInAggregateInput!] + NOT: typeAActedInAggregateInput + OR: [typeAActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: relTypeAggregationWhereInput + node: typeAActedInNodeAggregationWhereInput + } + + input typeAActedInConnectFieldInput { + connect: [typeBConnectInput!] + edge: relTypeCreateInput! + where: typeBConnectWhere + } + + type typeAActedInConnection { + edges: [typeAActedInRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input typeAActedInConnectionFilters { + \\"\\"\\" + Return typeAS where all of the related typeAActedInConnections match this filter + \\"\\"\\" + all: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where none of the related typeAActedInConnections match this filter + \\"\\"\\" + none: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where one of the related typeAActedInConnections match this filter + \\"\\"\\" + single: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where some of the related typeAActedInConnections match this filter + \\"\\"\\" + some: typeAActedInConnectionWhere + } + + input typeAActedInConnectionSort { + edge: relTypeSort + node: typeBSort + } + + input typeAActedInConnectionWhere { + AND: [typeAActedInConnectionWhere!] + NOT: typeAActedInConnectionWhere + OR: [typeAActedInConnectionWhere!] + edge: relTypeWhere + node: typeBWhere + } + + input typeAActedInCreateFieldInput { + edge: relTypeCreateInput! + node: typeBCreateInput! + } + + input typeAActedInDeleteFieldInput { + delete: typeBDeleteInput + where: typeAActedInConnectionWhere + } + + input typeAActedInDisconnectFieldInput { + disconnect: typeBDisconnectInput + where: typeAActedInConnectionWhere + } + + input typeAActedInFieldInput { + connect: [typeAActedInConnectFieldInput!] + create: [typeAActedInCreateFieldInput!] + } + + input typeAActedInNodeAggregationWhereInput { + AND: [typeAActedInNodeAggregationWhereInput!] + NOT: typeAActedInNodeAggregationWhereInput + OR: [typeAActedInNodeAggregationWhereInput!] + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + duration: DurationScalarAggregationFilters + duration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { eq: ... } } }' instead.\\") + duration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gt: ... } } }' instead.\\") + duration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gte: ... } } }' instead.\\") + duration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lt: ... } } }' instead.\\") + duration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lte: ... } } }' instead.\\") + duration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { eq: ... } } }' instead.\\") + duration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gt: ... } } }' instead.\\") + duration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gte: ... } } }' instead.\\") + duration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lt: ... } } }' instead.\\") + duration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lte: ... } } }' instead.\\") + duration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { eq: ... } } }' instead.\\") + duration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gt: ... } } }' instead.\\") + duration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gte: ... } } }' instead.\\") + duration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lt: ... } } }' instead.\\") + duration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lte: ... } } }' instead.\\") + localDateTime: LocalDateTimeScalarAggregationFilters + localDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { eq: ... } } }' instead.\\") + localDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gt: ... } } }' instead.\\") + localDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gte: ... } } }' instead.\\") + localDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lt: ... } } }' instead.\\") + localDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lte: ... } } }' instead.\\") + localDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { eq: ... } } }' instead.\\") + localDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gt: ... } } }' instead.\\") + localDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gte: ... } } }' instead.\\") + localDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lt: ... } } }' instead.\\") + localDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lte: ... } } }' instead.\\") + localTime: LocalTimeScalarAggregationFilters + localTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { eq: ... } } }' instead.\\") + localTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gt: ... } } }' instead.\\") + localTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gte: ... } } }' instead.\\") + localTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lt: ... } } }' instead.\\") + localTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lte: ... } } }' instead.\\") + localTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { eq: ... } } }' instead.\\") + localTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gt: ... } } }' instead.\\") + localTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gte: ... } } }' instead.\\") + localTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lt: ... } } }' instead.\\") + localTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lte: ... } } }' instead.\\") + time: TimeScalarAggregationFilters + time_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { eq: ... } } }' instead.\\") + time_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gt: ... } } }' instead.\\") + time_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gte: ... } } }' instead.\\") + time_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lt: ... } } }' instead.\\") + time_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lte: ... } } }' instead.\\") + time_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { eq: ... } } }' instead.\\") + time_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gt: ... } } }' instead.\\") + time_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gte: ... } } }' instead.\\") + time_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lt: ... } } }' instead.\\") + time_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lte: ... } } }' instead.\\") + } + + type typeAActedInRelationship { + cursor: String! + node: typeB! + properties: relType! + } + + input typeAActedInUpdateConnectionInput { + edge: relTypeUpdateInput + node: typeBUpdateInput + } + + input typeAActedInUpdateFieldInput { + connect: [typeAActedInConnectFieldInput!] + create: [typeAActedInCreateFieldInput!] + delete: [typeAActedInDeleteFieldInput!] + disconnect: [typeAActedInDisconnectFieldInput!] + update: typeAActedInUpdateConnectionInput + where: typeAActedInConnectionWhere + } + + type typeAAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input typeAConnectInput { + actedIn: [typeAActedInConnectFieldInput!] + } + + input typeAConnectWhere { + node: typeAWhere! + } + + input typeACreateInput { + actedIn: typeAActedInFieldInput + name: String + } + + input typeADeleteInput { + actedIn: [typeAActedInDeleteFieldInput!] + } + + input typeADisconnectInput { + actedIn: [typeAActedInDisconnectFieldInput!] + } + + type typeAEdge { + cursor: String! + node: typeA! + } + + input typeARelationshipFilters { + \\"\\"\\"Filter type where all of the related typeAS match this filter\\"\\"\\" + all: typeAWhere + \\"\\"\\"Filter type where none of the related typeAS match this filter\\"\\"\\" + none: typeAWhere + \\"\\"\\"Filter type where one of the related typeAS match this filter\\"\\"\\" + single: typeAWhere + \\"\\"\\"Filter type where some of the related typeAS match this filter\\"\\"\\" + some: typeAWhere + } + + \\"\\"\\" + Fields to sort TypeAS by. The order in which sorts are applied is not guaranteed when specifying many fields in one typeASort object. + \\"\\"\\" + input typeASort { + name: SortDirection + } + + input typeAUpdateInput { + actedIn: [typeAActedInUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input typeAWhere { + AND: [typeAWhere!] + NOT: typeAWhere + OR: [typeAWhere!] + actedIn: typeBRelationshipFilters + actedInAggregate: typeAActedInAggregateInput + actedInConnection: typeAActedInConnectionFilters + \\"\\"\\" + Return typeAS where all of the related typeAActedInConnections match this filter + \\"\\"\\" + actedInConnection_ALL: typeAActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeAS where none of the related typeAActedInConnections match this filter + \\"\\"\\" + actedInConnection_NONE: typeAActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeAS where one of the related typeAActedInConnections match this filter + \\"\\"\\" + actedInConnection_SINGLE: typeAActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeAS where some of the related typeAActedInConnections match this filter + \\"\\"\\" + actedInConnection_SOME: typeAActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return typeAS where all of the related typeBS match this filter\\"\\"\\" + actedIn_ALL: typeBWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") + \\"\\"\\"Return typeAS where none of the related typeBS match this filter\\"\\"\\" + actedIn_NONE: typeBWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") + \\"\\"\\"Return typeAS where one of the related typeBS match this filter\\"\\"\\" + actedIn_SINGLE: typeBWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") + \\"\\"\\"Return typeAS where some of the related typeBS match this filter\\"\\"\\" + actedIn_SOME: typeBWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + } + + type typeAtypeBActedInAggregationSelection { + count: Int! + edge: typeAtypeBActedInEdgeAggregateSelection + node: typeAtypeBActedInNodeAggregateSelection + } + + type typeAtypeBActedInEdgeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeAtypeBActedInNodeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeB implements interfaceC { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + id: ID! + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: Point + ratings: [Float!]! + rels(limit: Int, offset: Int, sort: [typeASort!], where: typeAWhere): [typeA!]! + relsAggregate(where: typeAWhere): typeBtypeARelsAggregationSelection + relsConnection(after: String, first: Int, sort: [typeBRelsConnectionSort!], where: typeBRelsConnectionWhere): typeBRelsConnection! + time: Time + } + + type typeBAggregateSelection { + averageRating: FloatAggregateSelection! + count: Int! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + input typeBConnectInput { + rels: [typeBRelsConnectFieldInput!] + } + + input typeBConnectWhere { + node: typeBWhere! + } + + input typeBCreateInput { + averageRating: Float! + cartesianPoint: CartesianPointInput + createdAt: DateTime + date: Date + duration: Duration + id: ID! + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: PointInput + ratings: [Float!]! + rels: typeBRelsFieldInput + time: Time + } + + input typeBDeleteInput { + rels: [typeBRelsDeleteFieldInput!] + } + + input typeBDisconnectInput { + rels: [typeBRelsDisconnectFieldInput!] + } + + type typeBEdge { + cursor: String! + node: typeB! + } + + input typeBRelationshipFilters { + \\"\\"\\"Filter type where all of the related typeBS match this filter\\"\\"\\" + all: typeBWhere + \\"\\"\\"Filter type where none of the related typeBS match this filter\\"\\"\\" + none: typeBWhere + \\"\\"\\"Filter type where one of the related typeBS match this filter\\"\\"\\" + single: typeBWhere + \\"\\"\\"Filter type where some of the related typeBS match this filter\\"\\"\\" + some: typeBWhere + } + + input typeBRelsAggregateInput { + AND: [typeBRelsAggregateInput!] + NOT: typeBRelsAggregateInput + OR: [typeBRelsAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: relTypeAggregationWhereInput + node: typeBRelsNodeAggregationWhereInput + } + + input typeBRelsConnectFieldInput { + connect: [typeAConnectInput!] + edge: relTypeCreateInput! + where: typeAConnectWhere + } + + type typeBRelsConnection { + edges: [typeBRelsRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input typeBRelsConnectionFilters { + \\"\\"\\" + Return typeBS where all of the related typeBRelsConnections match this filter + \\"\\"\\" + all: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where none of the related typeBRelsConnections match this filter + \\"\\"\\" + none: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where one of the related typeBRelsConnections match this filter + \\"\\"\\" + single: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where some of the related typeBRelsConnections match this filter + \\"\\"\\" + some: typeBRelsConnectionWhere + } + + input typeBRelsConnectionSort { + edge: relTypeSort + node: typeASort + } + + input typeBRelsConnectionWhere { + AND: [typeBRelsConnectionWhere!] + NOT: typeBRelsConnectionWhere + OR: [typeBRelsConnectionWhere!] + edge: relTypeWhere + node: typeAWhere + } + + input typeBRelsCreateFieldInput { + edge: relTypeCreateInput! + node: typeACreateInput! + } + + input typeBRelsDeleteFieldInput { + delete: typeADeleteInput + where: typeBRelsConnectionWhere + } + + input typeBRelsDisconnectFieldInput { + disconnect: typeADisconnectInput + where: typeBRelsConnectionWhere + } + + input typeBRelsFieldInput { + connect: [typeBRelsConnectFieldInput!] + create: [typeBRelsCreateFieldInput!] + } + + input typeBRelsNodeAggregationWhereInput { + AND: [typeBRelsNodeAggregationWhereInput!] + NOT: typeBRelsNodeAggregationWhereInput + OR: [typeBRelsNodeAggregationWhereInput!] + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + } + + type typeBRelsRelationship { + cursor: String! + node: typeA! + properties: relType! + } + + input typeBRelsUpdateConnectionInput { + edge: relTypeUpdateInput + node: typeAUpdateInput + } + + input typeBRelsUpdateFieldInput { + connect: [typeBRelsConnectFieldInput!] + create: [typeBRelsCreateFieldInput!] + delete: [typeBRelsDeleteFieldInput!] + disconnect: [typeBRelsDisconnectFieldInput!] + update: typeBRelsUpdateConnectionInput + where: typeBRelsConnectionWhere + } + + \\"\\"\\" + Fields to sort TypeBS by. The order in which sorts are applied is not guaranteed when specifying many fields in one typeBSort object. + \\"\\"\\" + input typeBSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + id: SortDirection + localDateTime: SortDirection + localTime: SortDirection + point: SortDirection + time: SortDirection + } + + input typeBUpdateInput { + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + cartesianPoint: CartesianPointMutations + cartesianPoint_SET: CartesianPointInput @deprecated(reason: \\"Please use the generic mutation 'cartesianPoint: { set: ... } }' instead.\\") + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + date: DateScalarMutations + date_SET: Date @deprecated(reason: \\"Please use the generic mutation 'date: { set: ... } }' instead.\\") + duration: DurationScalarMutations + duration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'duration: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + list: ListStringMutations + list_POP: Int @deprecated(reason: \\"Please use the generic mutation 'list: { pop: ... } }' instead.\\") + list_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'list: { push: ... } }' instead.\\") + list_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'list: { set: ... } }' instead.\\") + localDateTime: LocalDateTimeScalarMutations + localDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'localDateTime: { set: ... } }' instead.\\") + localTime: LocalTimeScalarMutations + localTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'localTime: { set: ... } }' instead.\\") + point: PointMutations + point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'point: { set: ... } }' instead.\\") + ratings: ListFloatMutations + ratings_POP: Int @deprecated(reason: \\"Please use the generic mutation 'ratings: { pop: ... } }' instead.\\") + ratings_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { push: ... } }' instead.\\") + ratings_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { set: ... } }' instead.\\") + rels: [typeBRelsUpdateFieldInput!] + time: TimeScalarMutations + time_SET: Time @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") + } + + input typeBWhere { + AND: [typeBWhere!] + NOT: typeBWhere + OR: [typeBWhere!] + averageRating: FloatScalarFilters + cartesianPoint: CartesianPointFilters + createdAt: DateTimeScalarFilters + date: DateScalarFilters + duration: DurationScalarFilters + id: IDScalarFilters + list: StringListFilters + localDateTime: LocalDateTimeScalarFilters + localTime: LocalTimeScalarFilters + point: PointFilters + ratings: FloatListFilters + rels: typeARelationshipFilters + relsAggregate: typeBRelsAggregateInput + relsConnection: typeBRelsConnectionFilters + \\"\\"\\" + Return typeBS where all of the related typeBRelsConnections match this filter + \\"\\"\\" + relsConnection_ALL: typeBRelsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relsConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeBS where none of the related typeBRelsConnections match this filter + \\"\\"\\" + relsConnection_NONE: typeBRelsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relsConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeBS where one of the related typeBRelsConnections match this filter + \\"\\"\\" + relsConnection_SINGLE: typeBRelsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relsConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return typeBS where some of the related typeBRelsConnections match this filter + \\"\\"\\" + relsConnection_SOME: typeBRelsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'relsConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return typeBS where all of the related typeAS match this filter\\"\\"\\" + rels_ALL: typeAWhere @deprecated(reason: \\"Please use the relevant generic filter 'rels: { all: ... }' instead.\\") + \\"\\"\\"Return typeBS where none of the related typeAS match this filter\\"\\"\\" + rels_NONE: typeAWhere @deprecated(reason: \\"Please use the relevant generic filter 'rels: { none: ... }' instead.\\") + \\"\\"\\"Return typeBS where one of the related typeAS match this filter\\"\\"\\" + rels_SINGLE: typeAWhere @deprecated(reason: \\"Please use the relevant generic filter 'rels: { single: ... }' instead.\\") + \\"\\"\\"Return typeBS where some of the related typeAS match this filter\\"\\"\\" + rels_SOME: typeAWhere @deprecated(reason: \\"Please use the relevant generic filter 'rels: { some: ... }' instead.\\") + time: TimeScalarFilters + } + + type typeBtypeARelsAggregationSelection { + count: Int! + edge: typeBtypeARelsEdgeAggregateSelection + node: typeBtypeARelsNodeAggregateSelection + } + + type typeBtypeARelsEdgeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeBtypeARelsNodeAggregateSelection { + name: StringAggregateSelection! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/remove-deprecated/comments.test.ts b/packages/graphql/tests/schema/remove-deprecated/comments.test.ts deleted file mode 100644 index 7948b41902..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/comments.test.ts +++ /dev/null @@ -1,1700 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Comments", () => { - test("Simple", async () => { - const typeDefs = gql` - "A custom scalar." - scalar CustomScalar - - "An enumeration of movie genres." - enum Genre { - ACTION - DRAMA - ROMANCE - } - - """ - A type describing a movie. - """ - type Movie @node { - id: ID - "The number of actors who acted in the movie." - actorCount: Int - """ - The average rating for the movie. - """ - averageRating: Float - """ - Is the movie active? - - This is measured based on annual profit. - """ - isActive: Boolean - genre: Genre - customScalar: CustomScalar - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\"A custom scalar.\\"\\"\\" - scalar CustomScalar - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type FloatAggregateSelection { - average: Float - max: Float - min: Float - sum: Float - } - - \\"\\"\\"An enumeration of movie genres.\\"\\"\\" - enum Genre { - ACTION - DRAMA - ROMANCE - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - \\"\\"\\"A type describing a movie.\\"\\"\\" - type Movie { - \\"\\"\\"The number of actors who acted in the movie.\\"\\"\\" - actorCount: Int - \\"\\"\\"The average rating for the movie.\\"\\"\\" - averageRating: Float - customScalar: CustomScalar - genre: Genre - id: ID - \\"\\"\\" - Is the movie active? - - This is measured based on annual profit. - \\"\\"\\" - isActive: Boolean - } - - type MovieAggregateSelection { - actorCount: IntAggregateSelection! - averageRating: FloatAggregateSelection! - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieCreateInput { - actorCount: Int - averageRating: Float - customScalar: CustomScalar - genre: Genre - id: ID - isActive: Boolean - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - actorCount: SortDirection - averageRating: SortDirection - customScalar: SortDirection - genre: SortDirection - id: SortDirection - isActive: SortDirection - } - - input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - customScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _SET field\\") - customScalar_SET: CustomScalar - genre: Genre @deprecated(reason: \\"Please use the explicit _SET field\\") - genre_SET: Genre - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - customScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _EQ version\\") - customScalar_EQ: CustomScalar - customScalar_IN: [CustomScalar] - genre: Genre @deprecated(reason: \\"Please use the explicit _EQ version\\") - genre_EQ: Genre - genre_IN: [Genre] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteMovies(where: MovieWhere): DeleteInfo! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - describe("Relationship", () => { - test("Simple", async () => { - const typeDefs = gql` - type Actor @node { - name: String - } - - type Movie @node { - id: ID - "Actors in Movie" - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - name: String - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - name: String - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - \\"\\"\\"Actors in Movie\\"\\"\\" - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - id: ID - } - - type MovieActorActorsAggregationSelection { - count: Int! - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - } - - input MovieActorsUpdateConnectionInput { - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - id: ID - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(where: ActorWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - - test("Interface", async () => { - const typeDefs = gql` - interface Production { - title: String! - } - - type Movie implements Production @node { - title: String! - runtime: Int! - } - - type Series implements Production @node { - title: String! - episodes: Int! - } - - type ActedIn @relationshipProperties { - screenTime: Int! - } - - type Actor @node { - name: String! - "Acted in Production" - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.actedIn - \\"\\"\\" - type ActedIn { - screenTime: Int! - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int - } - - input ActedInCreateInput { - screenTime: Int! - } - - input ActedInSort { - screenTime: SortDirection - } - - input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - } - - type Actor { - \\"\\"\\"Acted in Production\\"\\"\\" - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): ActorProductionActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! - name: String! - } - - input ActorActedInAggregateInput { - AND: [ActorActedInAggregateInput!] - NOT: ActorActedInAggregateInput - OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorActedInNodeAggregationWhereInput - } - - input ActorActedInConnectFieldInput { - edge: ActedInCreateInput! - where: ProductionConnectWhere - } - - type ActorActedInConnection { - edges: [ActorActedInRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorActedInConnectionSort { - edge: ActedInSort - node: ProductionSort - } - - input ActorActedInConnectionWhere { - AND: [ActorActedInConnectionWhere!] - NOT: ActorActedInConnectionWhere - OR: [ActorActedInConnectionWhere!] - edge: ActedInWhere - node: ProductionWhere - } - - input ActorActedInCreateFieldInput { - edge: ActedInCreateInput! - node: ProductionCreateInput! - } - - input ActorActedInDeleteFieldInput { - where: ActorActedInConnectionWhere - } - - input ActorActedInDisconnectFieldInput { - where: ActorActedInConnectionWhere - } - - input ActorActedInFieldInput { - connect: [ActorActedInConnectFieldInput!] - create: [ActorActedInCreateFieldInput!] - } - - input ActorActedInNodeAggregationWhereInput { - AND: [ActorActedInNodeAggregationWhereInput!] - NOT: ActorActedInNodeAggregationWhereInput - OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorActedInRelationship { - cursor: String! - node: Production! - properties: ActedIn! - } - - input ActorActedInUpdateConnectionInput { - edge: ActedInUpdateInput - node: ProductionUpdateInput - } - - input ActorActedInUpdateFieldInput { - connect: [ActorActedInConnectFieldInput!] - create: [ActorActedInCreateFieldInput!] - delete: [ActorActedInDeleteFieldInput!] - disconnect: [ActorActedInDisconnectFieldInput!] - update: ActorActedInUpdateConnectionInput - where: ActorActedInConnectionWhere - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - actedIn: ActorActedInFieldInput - name: String! - } - - input ActorDeleteInput { - actedIn: [ActorActedInDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - type ActorProductionActedInAggregationSelection { - count: Int! - edge: ActorProductionActedInEdgeAggregateSelection - node: ActorProductionActedInNodeAggregateSelection - } - - type ActorProductionActedInEdgeAggregateSelection { - screenTime: IntAggregateSelection! - } - - type ActorProductionActedInNodeAggregateSelection { - title: StringAggregateSelection! - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - actedInAggregate: ActorActedInAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorActedInConnections match this filter - \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere - \\"\\"\\"Return Actors where all of the related Productions match this filter\\"\\"\\" - actedIn_ALL: ProductionWhere - \\"\\"\\"Return Actors where none of the related Productions match this filter\\"\\"\\" - actedIn_NONE: ProductionWhere - \\"\\"\\"Return Actors where one of the related Productions match this filter\\"\\"\\" - actedIn_SINGLE: ProductionWhere - \\"\\"\\"Return Actors where some of the related Productions match this filter\\"\\"\\" - actedIn_SOME: ProductionWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements Production { - runtime: Int! - title: String! - } - - type MovieAggregateSelection { - count: Int! - runtime: IntAggregateSelection! - title: StringAggregateSelection! - } - - input MovieCreateInput { - runtime: Int! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - runtime: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - runtime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - runtime_DECREMENT: Int - runtime_INCREMENT: Int - runtime_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - runtime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - runtime_EQ: Int - runtime_GT: Int - runtime_GTE: Int - runtime_IN: [Int!] - runtime_LT: Int - runtime_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - deleteSeries(where: SeriesWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - interface Production { - title: String! - } - - type ProductionAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - input ProductionConnectWhere { - node: ProductionWhere! - } - - input ProductionCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - type ProductionEdge { - cursor: String! - node: Production! - } - - enum ProductionImplementation { - Movie - Series - } - - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] - } - - \\"\\"\\" - Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. - \\"\\"\\" - input ProductionSort { - title: SortDirection - } - - input ProductionUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input ProductionWhere { - AND: [ProductionWhere!] - NOT: ProductionWhere - OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type ProductionsConnection { - edges: [ProductionEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! - productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements Production { - episodes: Int! - title: String! - } - - type SeriesAggregateSelection { - count: Int! - episodes: IntAggregateSelection! - title: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - episodes: Int! - title: String! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - episodes: SortDirection - title: SortDirection - } - - input SeriesUpdateInput { - episodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episodes_DECREMENT: Int - episodes_INCREMENT: Int - episodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - episodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episodes_EQ: Int - episodes_GT: Int - episodes_GTE: Int - episodes_IN: [Int!] - episodes_LT: Int - episodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); - - test("Unions", async () => { - const typeDefs = gql` - union Search = Movie | Genre - - type Genre @node { - id: ID - } - - type Movie @node { - id: ID - search: [Search!]! @relationship(type: "SEARCH", direction: OUT) - searchNoDirective: Search - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Genre { - id: ID - } - - type GenreAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input GenreConnectWhere { - node: GenreWhere! - } - - input GenreCreateInput { - id: ID - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - id: SortDirection - } - - input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - id: ID - search(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - searchConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieSearchConnectionWhere): MovieSearchConnection! - searchNoDirective: Search - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieConnectInput { - search: MovieSearchConnectInput - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - id: ID - search: MovieSearchCreateInput - } - - input MovieDeleteInput { - search: MovieSearchDeleteInput - } - - input MovieDisconnectInput { - search: MovieSearchDisconnectInput - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - input MovieSearchConnectInput { - Genre: [MovieSearchGenreConnectFieldInput!] - Movie: [MovieSearchMovieConnectFieldInput!] - } - - type MovieSearchConnection { - edges: [MovieSearchRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieSearchConnectionWhere { - Genre: MovieSearchGenreConnectionWhere - Movie: MovieSearchMovieConnectionWhere - } - - input MovieSearchCreateInput { - Genre: MovieSearchGenreFieldInput - Movie: MovieSearchMovieFieldInput - } - - input MovieSearchDeleteInput { - Genre: [MovieSearchGenreDeleteFieldInput!] - Movie: [MovieSearchMovieDeleteFieldInput!] - } - - input MovieSearchDisconnectInput { - Genre: [MovieSearchGenreDisconnectFieldInput!] - Movie: [MovieSearchMovieDisconnectFieldInput!] - } - - input MovieSearchGenreConnectFieldInput { - where: GenreConnectWhere - } - - input MovieSearchGenreConnectionWhere { - AND: [MovieSearchGenreConnectionWhere!] - NOT: MovieSearchGenreConnectionWhere - OR: [MovieSearchGenreConnectionWhere!] - node: GenreWhere - } - - input MovieSearchGenreCreateFieldInput { - node: GenreCreateInput! - } - - input MovieSearchGenreDeleteFieldInput { - where: MovieSearchGenreConnectionWhere - } - - input MovieSearchGenreDisconnectFieldInput { - where: MovieSearchGenreConnectionWhere - } - - input MovieSearchGenreFieldInput { - connect: [MovieSearchGenreConnectFieldInput!] - create: [MovieSearchGenreCreateFieldInput!] - } - - input MovieSearchGenreUpdateConnectionInput { - node: GenreUpdateInput - } - - input MovieSearchGenreUpdateFieldInput { - connect: [MovieSearchGenreConnectFieldInput!] - create: [MovieSearchGenreCreateFieldInput!] - delete: [MovieSearchGenreDeleteFieldInput!] - disconnect: [MovieSearchGenreDisconnectFieldInput!] - update: MovieSearchGenreUpdateConnectionInput - where: MovieSearchGenreConnectionWhere - } - - input MovieSearchMovieConnectFieldInput { - connect: [MovieConnectInput!] - where: MovieConnectWhere - } - - input MovieSearchMovieConnectionWhere { - AND: [MovieSearchMovieConnectionWhere!] - NOT: MovieSearchMovieConnectionWhere - OR: [MovieSearchMovieConnectionWhere!] - node: MovieWhere - } - - input MovieSearchMovieCreateFieldInput { - node: MovieCreateInput! - } - - input MovieSearchMovieDeleteFieldInput { - delete: MovieDeleteInput - where: MovieSearchMovieConnectionWhere - } - - input MovieSearchMovieDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: MovieSearchMovieConnectionWhere - } - - input MovieSearchMovieFieldInput { - connect: [MovieSearchMovieConnectFieldInput!] - create: [MovieSearchMovieCreateFieldInput!] - } - - input MovieSearchMovieUpdateConnectionInput { - node: MovieUpdateInput - } - - input MovieSearchMovieUpdateFieldInput { - connect: [MovieSearchMovieConnectFieldInput!] - create: [MovieSearchMovieCreateFieldInput!] - delete: [MovieSearchMovieDeleteFieldInput!] - disconnect: [MovieSearchMovieDisconnectFieldInput!] - update: MovieSearchMovieUpdateConnectionInput - where: MovieSearchMovieConnectionWhere - } - - type MovieSearchRelationship { - cursor: String! - node: Search! - } - - input MovieSearchUpdateInput { - Genre: [MovieSearchGenreUpdateFieldInput!] - Movie: [MovieSearchMovieUpdateFieldInput!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - } - - input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - search: MovieSearchUpdateInput - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - \\"\\"\\" - Return Movies where all of the related MovieSearchConnections match this filter - \\"\\"\\" - searchConnection_ALL: MovieSearchConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieSearchConnections match this filter - \\"\\"\\" - searchConnection_NONE: MovieSearchConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieSearchConnections match this filter - \\"\\"\\" - searchConnection_SINGLE: MovieSearchConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieSearchConnections match this filter - \\"\\"\\" - searchConnection_SOME: MovieSearchConnectionWhere - \\"\\"\\"Return Movies where all of the related Searches match this filter\\"\\"\\" - search_ALL: SearchWhere - \\"\\"\\"Return Movies where none of the related Searches match this filter\\"\\"\\" - search_NONE: SearchWhere - \\"\\"\\"Return Movies where one of the related Searches match this filter\\"\\"\\" - search_SINGLE: SearchWhere - \\"\\"\\"Return Movies where some of the related Searches match this filter\\"\\"\\" - search_SOME: SearchWhere - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteGenres(where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - searches(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - union Search = Genre | Movie - - input SearchWhere { - Genre: GenreWhere - Movie: MovieWhere - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts b/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts deleted file mode 100644 index 285aaeb702..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/connect-or-create.test.ts +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Connect Or Create", () => { - test("Connect Or Create", async () => { - const typeDefs = gql` - type Movie @node { - title: String! - isan: String! @unique - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { excludeDeprecatedFields: { connectOrCreate: true } }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesNodeAggregateSelection { - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - isan_AVERAGE_LENGTH_EQUAL: Float - isan_AVERAGE_LENGTH_GT: Float - isan_AVERAGE_LENGTH_GTE: Float - isan_AVERAGE_LENGTH_LT: Float - isan_AVERAGE_LENGTH_LTE: Float - isan_LONGEST_LENGTH_EQUAL: Int - isan_LONGEST_LENGTH_GT: Int - isan_LONGEST_LENGTH_GTE: Int - isan_LONGEST_LENGTH_LT: Int - isan_LONGEST_LENGTH_LTE: Int - isan_SHORTEST_LENGTH_EQUAL: Int - isan_SHORTEST_LENGTH_GT: Int - isan_SHORTEST_LENGTH_GTE: Int - isan_SHORTEST_LENGTH_LT: Int - isan_SHORTEST_LENGTH_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - } - - input ActorMoviesUpdateConnectionInput { - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Movie { - isan: String! - title: String! - } - - type MovieAggregateSelection { - count: Int! - isan: StringAggregateSelection! - title: StringAggregateSelection! - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - isan: String! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - isan: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - isan: String @deprecated(reason: \\"Please use the explicit _SET field\\") - isan_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - isan: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - isan_CONTAINS: String - isan_ENDS_WITH: String - isan_EQ: String - isan_IN: [String!] - isan_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/directed-argument.test.ts b/packages/graphql/tests/schema/remove-deprecated/directed-argument.test.ts deleted file mode 100644 index 7c1ed7cbbf..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/directed-argument.test.ts +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Deprecated directed argument", () => { - test("should remove the directed argument", async () => { - const typeDefs = gql` - type Actor @node { - name: String! - movies: [Movie!]! - @relationship( - type: "ACTED_IN" - direction: OUT - properties: "ActedIn" - queryDirection: DEFAULT_UNDIRECTED - ) - } - - type Movie implements Production @node { - title: String! - actors: [Actor!]! - @relationship( - type: "ACTED_IN" - direction: IN - properties: "ActedIn" - queryDirection: DEFAULT_DIRECTED - ) - } - - type ActedIn @relationshipProperties { - screenTime: Int! - startDate: Date! - leadRole: Boolean! - } - union Search = Movie | Genre - - type Genre @node { - id: ID - } - - interface Production { - title: String! - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - excludeDeprecatedFields: { - directedArgument: true, - }, - }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - * Movie.actors - \\"\\"\\" - type ActedIn { - leadRole: Boolean! - screenTime: Int! - startDate: Date! - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int - } - - input ActedInCreateInput { - leadRole: Boolean! - screenTime: Int! - startDate: Date! - } - - input ActedInSort { - leadRole: SortDirection - screenTime: SortDirection - startDate: SortDirection - } - - input ActedInUpdateInput { - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - leadRole_SET: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _SET field\\") - startDate_SET: Date - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - leadRole_EQ: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _EQ version\\") - startDate_EQ: Date - startDate_GT: Date - startDate_GTE: Date - startDate_IN: [Date!] - startDate_LT: Date - startDate_LTE: Date - } - - type Actor { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectInput { - movies: [ActorMoviesConnectFieldInput!] - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - input ActorDisconnectInput { - movies: [ActorMoviesDisconnectFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - screenTime: IntAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - connect: [MovieConnectInput!] - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - delete: MovieDeleteInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" - scalar Date - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Genre { - id: ID - } - - type GenreAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input GenreCreateInput { - id: ID - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - id: SortDirection - } - - input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements Production { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - title: String! - } - - type MovieActorActorsAggregationSelection { - count: Int! - edge: MovieActorActorsEdgeAggregateSelection - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsEdgeAggregateSelection { - screenTime: IntAggregateSelection! - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - connect: [ActorConnectInput!] - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - edge: ActedInSort - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - edge: ActedInWhere - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - edge: ActedInCreateInput! - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - delete: ActorDeleteInput - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - disconnect: ActorDisconnectInput - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - properties: ActedIn! - } - - input MovieActorsUpdateConnectionInput { - edge: ActedInUpdateInput - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - input MovieConnectInput { - actors: [MovieActorsConnectFieldInput!] - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - title: String! - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - input MovieDisconnectInput { - actors: [MovieActorsDisconnectFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - title: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteGenres(where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - interface Production { - title: String! - } - - type ProductionAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - type ProductionEdge { - cursor: String! - node: Production! - } - - enum ProductionImplementation { - Movie - } - - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] - } - - \\"\\"\\" - Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. - \\"\\"\\" - input ProductionSort { - title: SortDirection - } - - input ProductionWhere { - AND: [ProductionWhere!] - NOT: ProductionWhere - OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type ProductionsConnection { - edges: [ProductionEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! - productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - searches(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - - union Search = Genre | Movie - - input SearchWhere { - Genre: GenreWhere - Movie: MovieWhere - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/implicit-equality.test.ts b/packages/graphql/tests/schema/remove-deprecated/implicit-equality.test.ts deleted file mode 100644 index 861eafb088..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/implicit-equality.test.ts +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Implicit Equality filters", () => { - test("Should remove implicit filters if specified by the setting implicitEqualFilters", async () => { - const typeDefs = /* GraphQL */ ` - type Actor @node { - name: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - type Movie @node { - id: ID - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - role: String - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - excludeDeprecatedFields: { - implicitEqualFilters: false, - deprecatedOptionsArgument: true, - }, - }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - * Movie.actors - \\"\\"\\" - type ActedIn { - role: String - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - role_AVERAGE_LENGTH_EQUAL: Float - role_AVERAGE_LENGTH_GT: Float - role_AVERAGE_LENGTH_GTE: Float - role_AVERAGE_LENGTH_LT: Float - role_AVERAGE_LENGTH_LTE: Float - role_LONGEST_LENGTH_EQUAL: Int - role_LONGEST_LENGTH_GT: Int - role_LONGEST_LENGTH_GTE: Int - role_LONGEST_LENGTH_LT: Int - role_LONGEST_LENGTH_LTE: Int - role_SHORTEST_LENGTH_EQUAL: Int - role_SHORTEST_LENGTH_GT: Int - role_SHORTEST_LENGTH_GTE: Int - role_SHORTEST_LENGTH_LT: Int - role_SHORTEST_LENGTH_LTE: Int - } - - input ActedInCreateInput { - role: String - } - - input ActedInSort { - role: SortDirection - } - - input ActedInUpdateInput { - role: String @deprecated(reason: \\"Please use the explicit _SET field\\") - role_SET: String - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - role: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - role_CONTAINS: String - role_ENDS_WITH: String - role_EQ: String - role_IN: [String] - role_STARTS_WITH: String - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectInput { - movies: [ActorMoviesConnectFieldInput!] - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - input ActorDisconnectInput { - movies: [ActorMoviesDisconnectFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - role: StringAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - connect: [MovieConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - delete: MovieDeleteInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - id: ID - } - - type MovieActorActorsAggregationSelection { - count: Int! - edge: MovieActorActorsEdgeAggregateSelection - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsEdgeAggregateSelection { - role: StringAggregateSelection! - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - connect: [ActorConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - edge: ActedInSort - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - edge: ActedInWhere - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - edge: ActedInCreateInput - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - delete: ActorDeleteInput - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - disconnect: ActorDisconnectInput - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - properties: ActedIn! - } - - input MovieActorsUpdateConnectionInput { - edge: ActedInUpdateInput - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieConnectInput { - actors: [MovieActorsConnectFieldInput!] - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - id: ID - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - input MovieDisconnectInput { - actors: [MovieActorsDisconnectFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/implicit-set.test.ts b/packages/graphql/tests/schema/remove-deprecated/implicit-set.test.ts deleted file mode 100644 index 6873ef3c9f..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/implicit-set.test.ts +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Implicit SET field", () => { - test("Should remove implicit _SET field if specified by the setting implicitSET", async () => { - const typeDefs = /* GraphQL */ ` - type Actor @node { - name: String - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - type Movie @node { - id: ID - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - role: String - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - excludeDeprecatedFields: { - implicitSet: true, - }, - }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - * Movie.actors - \\"\\"\\" - type ActedIn { - role: String - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - role_AVERAGE_LENGTH_EQUAL: Float - role_AVERAGE_LENGTH_GT: Float - role_AVERAGE_LENGTH_GTE: Float - role_AVERAGE_LENGTH_LT: Float - role_AVERAGE_LENGTH_LTE: Float - role_LONGEST_LENGTH_EQUAL: Int - role_LONGEST_LENGTH_GT: Int - role_LONGEST_LENGTH_GTE: Int - role_LONGEST_LENGTH_LT: Int - role_LONGEST_LENGTH_LTE: Int - role_SHORTEST_LENGTH_EQUAL: Int - role_SHORTEST_LENGTH_GT: Int - role_SHORTEST_LENGTH_GTE: Int - role_SHORTEST_LENGTH_LT: Int - role_SHORTEST_LENGTH_LTE: Int - } - - input ActedInCreateInput { - role: String - } - - input ActedInSort { - role: SortDirection - } - - input ActedInUpdateInput { - role: String @deprecated(reason: \\"Please use the explicit _SET field\\") - role_SET: String - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - role: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - role_CONTAINS: String - role_ENDS_WITH: String - role_EQ: String - role_IN: [String] - role_STARTS_WITH: String - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectInput { - movies: [ActorMoviesConnectFieldInput!] - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - input ActorDisconnectInput { - movies: [ActorMoviesDisconnectFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - role: StringAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - connect: [MovieConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - delete: MovieDeleteInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - id: ID - } - - type MovieActorActorsAggregationSelection { - count: Int! - edge: MovieActorActorsEdgeAggregateSelection - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsEdgeAggregateSelection { - role: StringAggregateSelection! - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - connect: [ActorConnectInput!] - edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - edge: ActedInSort - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - edge: ActedInWhere - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - edge: ActedInCreateInput - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - delete: ActorDeleteInput - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - disconnect: ActorDisconnectInput - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - properties: ActedIn! - } - - input MovieActorsUpdateConnectionInput { - edge: ActedInUpdateInput - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input MovieConnectInput { - actors: [MovieActorsConnectFieldInput!] - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - id: ID - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - input MovieDisconnectInput { - actors: [MovieActorsDisconnectFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - id_SET: ID - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/mutation-operations.test.ts b/packages/graphql/tests/schema/remove-deprecated/mutation-operations.test.ts new file mode 100644 index 0000000000..960283bfbd --- /dev/null +++ b/packages/graphql/tests/schema/remove-deprecated/mutation-operations.test.ts @@ -0,0 +1,920 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../../src"; + +describe("Deprecated mutation operations", () => { + test("Remove deprecated mutation operations", async () => { + const typeDefs = /* GraphQL */ ` + type Actor @node { + name: String + actedIn: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) + } + + type Movie @node { + id: ID! + ratings: [Float!]! + actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) + averageRating: Float! + date: Date + point: Point + } + + type ActedIn @relationshipProperties { + pay: [Float!] + value: Int + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { + excludeDeprecatedFields: { + mutationOperations: true, + }, + }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\" + The edge properties for the following fields: + * Actor.actedIn + * Movie.actors + \\"\\"\\" + type ActedIn { + pay: [Float!] + value: Int + } + + input ActedInAggregationWhereInput { + AND: [ActedInAggregationWhereInput!] + NOT: ActedInAggregationWhereInput + OR: [ActedInAggregationWhereInput!] + value: IntScalarAggregationFilters + value_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'value: { average: { eq: ... } } }' instead.\\") + value_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'value: { average: { gt: ... } } }' instead.\\") + value_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'value: { average: { gte: ... } } }' instead.\\") + value_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'value: { average: { lt: ... } } }' instead.\\") + value_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'value: { average: { lte: ... } } }' instead.\\") + value_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { max: { eq: ... } } }' instead.\\") + value_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { max: { gt: ... } } }' instead.\\") + value_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { max: { gte: ... } } }' instead.\\") + value_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { max: { lt: ... } } }' instead.\\") + value_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { max: { lte: ... } } }' instead.\\") + value_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { min: { eq: ... } } }' instead.\\") + value_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { min: { gt: ... } } }' instead.\\") + value_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { min: { gte: ... } } }' instead.\\") + value_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { min: { lt: ... } } }' instead.\\") + value_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { min: { lte: ... } } }' instead.\\") + value_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { sum: { eq: ... } } }' instead.\\") + value_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { sum: { gt: ... } } }' instead.\\") + value_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { sum: { gte: ... } } }' instead.\\") + value_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { sum: { lt: ... } } }' instead.\\") + value_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'value: { sum: { lte: ... } } }' instead.\\") + } + + input ActedInCreateInput { + pay: [Float!] + value: Int + } + + input ActedInSort { + pay: SortDirection + value: SortDirection + } + + input ActedInUpdateInput { + pay: ListFloatMutations + value: IntScalarMutations + } + + input ActedInWhere { + AND: [ActedInWhere!] + NOT: ActedInWhere + OR: [ActedInWhere!] + pay: FloatListFilters + pay_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter pay: { eq: ... }\\") + pay_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { includes: ... }\\") + value: IntScalarFilters + value_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter value: { eq: ... }\\") + value_GT: Int @deprecated(reason: \\"Please use the relevant generic filter value: { gt: ... }\\") + value_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter value: { gte: ... }\\") + value_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter value: { in: ... }\\") + value_LT: Int @deprecated(reason: \\"Please use the relevant generic filter value: { lt: ... }\\") + value_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter value: { lte: ... }\\") + } + + type Actor { + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + name: String + } + + input ActorActedInAggregateInput { + AND: [ActorActedInAggregateInput!] + NOT: ActorActedInAggregateInput + OR: [ActorActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: ActorActedInNodeAggregationWhereInput + } + + input ActorActedInConnectFieldInput { + connect: [MovieConnectInput!] + edge: ActedInCreateInput + where: MovieConnectWhere + } + + type ActorActedInConnection { + edges: [ActorActedInRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + + input ActorActedInConnectionSort { + edge: ActedInSort + node: MovieSort + } + + input ActorActedInConnectionWhere { + AND: [ActorActedInConnectionWhere!] + NOT: ActorActedInConnectionWhere + OR: [ActorActedInConnectionWhere!] + edge: ActedInWhere + node: MovieWhere + } + + input ActorActedInCreateFieldInput { + edge: ActedInCreateInput + node: MovieCreateInput! + } + + input ActorActedInDeleteFieldInput { + delete: MovieDeleteInput + where: ActorActedInConnectionWhere + } + + input ActorActedInDisconnectFieldInput { + disconnect: MovieDisconnectInput + where: ActorActedInConnectionWhere + } + + input ActorActedInFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + } + + input ActorActedInNodeAggregationWhereInput { + AND: [ActorActedInNodeAggregationWhereInput!] + NOT: ActorActedInNodeAggregationWhereInput + OR: [ActorActedInNodeAggregationWhereInput!] + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") + } + + type ActorActedInRelationship { + cursor: String! + node: Movie! + properties: ActedIn! + } + + input ActorActedInUpdateConnectionInput { + edge: ActedInUpdateInput + node: MovieUpdateInput + } + + input ActorActedInUpdateFieldInput { + connect: [ActorActedInConnectFieldInput!] + create: [ActorActedInCreateFieldInput!] + delete: [ActorActedInDeleteFieldInput!] + disconnect: [ActorActedInDisconnectFieldInput!] + update: ActorActedInUpdateConnectionInput + where: ActorActedInConnectionWhere + } + + type ActorAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input ActorConnectInput { + actedIn: [ActorActedInConnectFieldInput!] + } + + input ActorConnectWhere { + node: ActorWhere! + } + + input ActorCreateInput { + actedIn: ActorActedInFieldInput + name: String + } + + input ActorDeleteInput { + actedIn: [ActorActedInDeleteFieldInput!] + } + + input ActorDisconnectInput { + actedIn: [ActorActedInDisconnectFieldInput!] + } + + type ActorEdge { + cursor: String! + node: Actor! + } + + type ActorMovieActedInAggregationSelection { + count: Int! + edge: ActorMovieActedInEdgeAggregateSelection + node: ActorMovieActedInNodeAggregateSelection + } + + type ActorMovieActedInEdgeAggregateSelection { + value: IntAggregateSelection! + } + + type ActorMovieActedInNodeAggregateSelection { + averageRating: FloatAggregateSelection! + } + + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + + \\"\\"\\" + Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. + \\"\\"\\" + input ActorSort { + name: SortDirection + } + + input ActorUpdateInput { + actedIn: [ActorActedInUpdateFieldInput!] + name: StringScalarMutations + } + + input ActorWhere { + AND: [ActorWhere!] + NOT: ActorWhere + OR: [ActorWhere!] + actedIn: MovieRelationshipFilters + actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") + \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") + \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") + \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type ActorsConnection { + edges: [ActorEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type CreateActorsMutationResponse { + actors: [Actor!]! + info: CreateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateMoviesMutationResponse { + info: CreateInfo! + movies: [Movie!]! + } + + \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" + scalar Date + + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + + \\"\\"\\"Date mutations\\"\\"\\" + input DateScalarMutations { + set: Date + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + type IntAggregateSelection { + average: Float + max: Int + min: Int + sum: Int + } + + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + type Movie { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + averageRating: Float! + date: Date + id: ID! + point: Point + ratings: [Float!]! + } + + type MovieActorActorsAggregationSelection { + count: Int! + edge: MovieActorActorsEdgeAggregateSelection + node: MovieActorActorsNodeAggregateSelection + } + + type MovieActorActorsEdgeAggregateSelection { + value: IntAggregateSelection! + } + + type MovieActorActorsNodeAggregateSelection { + name: StringAggregateSelection! + } + + input MovieActorsAggregateInput { + AND: [MovieActorsAggregateInput!] + NOT: MovieActorsAggregateInput + OR: [MovieActorsAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: ActedInAggregationWhereInput + node: MovieActorsNodeAggregationWhereInput + } + + input MovieActorsConnectFieldInput { + connect: [ActorConnectInput!] + edge: ActedInCreateInput + where: ActorConnectWhere + } + + type MovieActorsConnection { + edges: [MovieActorsRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + + input MovieActorsConnectionSort { + edge: ActedInSort + node: ActorSort + } + + input MovieActorsConnectionWhere { + AND: [MovieActorsConnectionWhere!] + NOT: MovieActorsConnectionWhere + OR: [MovieActorsConnectionWhere!] + edge: ActedInWhere + node: ActorWhere + } + + input MovieActorsCreateFieldInput { + edge: ActedInCreateInput + node: ActorCreateInput! + } + + input MovieActorsDeleteFieldInput { + delete: ActorDeleteInput + where: MovieActorsConnectionWhere + } + + input MovieActorsDisconnectFieldInput { + disconnect: ActorDisconnectInput + where: MovieActorsConnectionWhere + } + + input MovieActorsFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + } + + input MovieActorsNodeAggregationWhereInput { + AND: [MovieActorsNodeAggregationWhereInput!] + NOT: MovieActorsNodeAggregationWhereInput + OR: [MovieActorsNodeAggregationWhereInput!] + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + } + + type MovieActorsRelationship { + cursor: String! + node: Actor! + properties: ActedIn! + } + + input MovieActorsUpdateConnectionInput { + edge: ActedInUpdateInput + node: ActorUpdateInput + } + + input MovieActorsUpdateFieldInput { + connect: [MovieActorsConnectFieldInput!] + create: [MovieActorsCreateFieldInput!] + delete: [MovieActorsDeleteFieldInput!] + disconnect: [MovieActorsDisconnectFieldInput!] + update: MovieActorsUpdateConnectionInput + where: MovieActorsConnectionWhere + } + + type MovieAggregateSelection { + averageRating: FloatAggregateSelection! + count: Int! + } + + input MovieConnectInput { + actors: [MovieActorsConnectFieldInput!] + } + + input MovieConnectWhere { + node: MovieWhere! + } + + input MovieCreateInput { + actors: MovieActorsFieldInput + averageRating: Float! + date: Date + id: ID! + point: PointInput + ratings: [Float!]! + } + + input MovieDeleteInput { + actors: [MovieActorsDeleteFieldInput!] + } + + input MovieDisconnectInput { + actors: [MovieActorsDisconnectFieldInput!] + } + + type MovieEdge { + cursor: String! + node: Movie! + } + + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere + } + + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + averageRating: SortDirection + date: SortDirection + id: SortDirection + point: SortDirection + } + + input MovieUpdateInput { + actors: [MovieActorsUpdateFieldInput!] + averageRating: FloatScalarMutations + date: DateScalarMutations + id: IDScalarMutations + point: PointMutations + ratings: ListFloatMutations + } + + input MovieWhere { + AND: [MovieWhere!] + NOT: MovieWhere + OR: [MovieWhere!] + actors: ActorRelationshipFilters + actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") + \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") + \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") + \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") + \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + date: DateScalarFilters + date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter date: { eq: ... }\\") + date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gt: ... }\\") + date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gte: ... }\\") + date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter date: { in: ... }\\") + date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lt: ... }\\") + date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + point: PointFilters + point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { distance: ... }\\") + point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter point: { eq: ... }\\") + point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gt: ... }\\") + point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gte: ... }\\") + point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter point: { in: ... }\\") + point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lt: ... }\\") + point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lte: ... }\\") + ratings: FloatListFilters + ratings_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter ratings: { eq: ... }\\") + ratings_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter ratings: { includes: ... }\\") + } + + type MoviesConnection { + edges: [MovieEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type Mutation { + createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! + createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! + deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! + deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! + updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! + updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + \\"\\"\\" + A point in a coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#point + \\"\\"\\" + type Point { + crs: String! + height: Float + latitude: Float! + longitude: Float! + srid: Int! + } + + \\"\\"\\"Input type for a point with a distance\\"\\"\\" + input PointDistance { + \\"\\"\\"The distance in metres to be used when comparing two points\\"\\"\\" + distance: Float! + point: PointInput! + } + + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + + \\"\\"\\"Input type for a point\\"\\"\\" + input PointInput { + height: Float + latitude: Float! + longitude: Float! + } + + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + + type Query { + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): ActorAggregateSelection! + actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): MovieAggregateSelection! + moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + type UpdateActorsMutationResponse { + actors: [Actor!]! + info: UpdateInfo! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateMoviesMutationResponse { + info: UpdateInfo! + movies: [Movie!]! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/remove-deprecated/options-argument.test.ts b/packages/graphql/tests/schema/remove-deprecated/options-argument.test.ts deleted file mode 100644 index b7dffb16f1..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/options-argument.test.ts +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Deprecated options argument", () => { - test("should remove the options argument", async () => { - const typeDefs = gql` - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - - type Movie implements Production @node { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - } - - type ActedIn @relationshipProperties { - screenTime: Int! - startDate: Date! - leadRole: Boolean! - } - union Search = Movie | Genre - - type Genre @node { - id: ID - } - - interface Production { - title: String! - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - excludeDeprecatedFields: { - deprecatedOptionsArgument: true, - }, - }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - The edge properties for the following fields: - * Actor.movies - * Movie.actors - \\"\\"\\" - type ActedIn { - leadRole: Boolean! - screenTime: Int! - startDate: Date! - } - - input ActedInAggregationWhereInput { - AND: [ActedInAggregationWhereInput!] - NOT: ActedInAggregationWhereInput - OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int - } - - input ActedInCreateInput { - leadRole: Boolean! - screenTime: Int! - startDate: Date! - } - - input ActedInSort { - leadRole: SortDirection - screenTime: SortDirection - startDate: SortDirection - } - - input ActedInUpdateInput { - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - leadRole_SET: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _SET field\\") - startDate_SET: Date - } - - input ActedInWhere { - AND: [ActedInWhere!] - NOT: ActedInWhere - OR: [ActedInWhere!] - leadRole: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - leadRole_EQ: Boolean - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int - startDate: Date @deprecated(reason: \\"Please use the explicit _EQ version\\") - startDate_EQ: Date - startDate_GT: Date - startDate_GTE: Date - startDate_IN: [Date!] - startDate_LT: Date - startDate_LTE: Date - } - - type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! - name: String! - } - - type ActorAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input ActorConnectInput { - movies: [ActorMoviesConnectFieldInput!] - } - - input ActorConnectWhere { - node: ActorWhere! - } - - input ActorCreateInput { - movies: ActorMoviesFieldInput - name: String! - } - - input ActorDeleteInput { - movies: [ActorMoviesDeleteFieldInput!] - } - - input ActorDisconnectInput { - movies: [ActorMoviesDisconnectFieldInput!] - } - - type ActorEdge { - cursor: String! - node: Actor! - } - - type ActorMovieMoviesAggregationSelection { - count: Int! - edge: ActorMovieMoviesEdgeAggregateSelection - node: ActorMovieMoviesNodeAggregateSelection - } - - type ActorMovieMoviesEdgeAggregateSelection { - screenTime: IntAggregateSelection! - } - - type ActorMovieMoviesNodeAggregateSelection { - title: StringAggregateSelection! - } - - input ActorMoviesAggregateInput { - AND: [ActorMoviesAggregateInput!] - NOT: ActorMoviesAggregateInput - OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: ActorMoviesNodeAggregationWhereInput - } - - input ActorMoviesConnectFieldInput { - connect: [MovieConnectInput!] - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: MovieConnectWhere - } - - type ActorMoviesConnection { - edges: [ActorMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ActorMoviesConnectionSort { - edge: ActedInSort - node: MovieSort - } - - input ActorMoviesConnectionWhere { - AND: [ActorMoviesConnectionWhere!] - NOT: ActorMoviesConnectionWhere - OR: [ActorMoviesConnectionWhere!] - edge: ActedInWhere - node: MovieWhere - } - - input ActorMoviesCreateFieldInput { - edge: ActedInCreateInput! - node: MovieCreateInput! - } - - input ActorMoviesDeleteFieldInput { - delete: MovieDeleteInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesDisconnectFieldInput { - disconnect: MovieDisconnectInput - where: ActorMoviesConnectionWhere - } - - input ActorMoviesFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - } - - input ActorMoviesNodeAggregationWhereInput { - AND: [ActorMoviesNodeAggregationWhereInput!] - NOT: ActorMoviesNodeAggregationWhereInput - OR: [ActorMoviesNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int - } - - type ActorMoviesRelationship { - cursor: String! - node: Movie! - properties: ActedIn! - } - - input ActorMoviesUpdateConnectionInput { - edge: ActedInUpdateInput - node: MovieUpdateInput - } - - input ActorMoviesUpdateFieldInput { - connect: [ActorMoviesConnectFieldInput!] - create: [ActorMoviesCreateFieldInput!] - delete: [ActorMoviesDeleteFieldInput!] - disconnect: [ActorMoviesDisconnectFieldInput!] - update: ActorMoviesUpdateConnectionInput - where: ActorMoviesConnectionWhere - } - - \\"\\"\\" - Fields to sort Actors by. The order in which sorts are applied is not guaranteed when specifying many fields in one ActorSort object. - \\"\\"\\" - input ActorSort { - name: SortDirection - } - - input ActorUpdateInput { - movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - input ActorWhere { - AND: [ActorWhere!] - NOT: ActorWhere - OR: [ActorWhere!] - moviesAggregate: ActorMoviesAggregateInput - \\"\\"\\" - Return Actors where all of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where none of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where one of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere - \\"\\"\\" - Return Actors where some of the related ActorMoviesConnections match this filter - \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere - \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere - \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere - \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere - \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type ActorsConnection { - edges: [ActorEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateActorsMutationResponse { - actors: [Actor!]! - info: CreateInfo! - } - - type CreateGenresMutationResponse { - genres: [Genre!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" - scalar Date - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Genre { - id: ID - } - - type GenreAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input GenreCreateInput { - id: ID - } - - type GenreEdge { - cursor: String! - node: Genre! - } - - \\"\\"\\" - Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. - \\"\\"\\" - input GenreSort { - id: SortDirection - } - - input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input GenreWhere { - AND: [GenreWhere!] - NOT: GenreWhere - OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - } - - type GenresConnection { - edges: [GenreEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements Production { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - title: String! - } - - type MovieActorActorsAggregationSelection { - count: Int! - edge: MovieActorActorsEdgeAggregateSelection - node: MovieActorActorsNodeAggregateSelection - } - - type MovieActorActorsEdgeAggregateSelection { - screenTime: IntAggregateSelection! - } - - type MovieActorActorsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input MovieActorsAggregateInput { - AND: [MovieActorsAggregateInput!] - NOT: MovieActorsAggregateInput - OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - edge: ActedInAggregationWhereInput - node: MovieActorsNodeAggregationWhereInput - } - - input MovieActorsConnectFieldInput { - connect: [ActorConnectInput!] - edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: ActorConnectWhere - } - - type MovieActorsConnection { - edges: [MovieActorsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input MovieActorsConnectionSort { - edge: ActedInSort - node: ActorSort - } - - input MovieActorsConnectionWhere { - AND: [MovieActorsConnectionWhere!] - NOT: MovieActorsConnectionWhere - OR: [MovieActorsConnectionWhere!] - edge: ActedInWhere - node: ActorWhere - } - - input MovieActorsCreateFieldInput { - edge: ActedInCreateInput! - node: ActorCreateInput! - } - - input MovieActorsDeleteFieldInput { - delete: ActorDeleteInput - where: MovieActorsConnectionWhere - } - - input MovieActorsDisconnectFieldInput { - disconnect: ActorDisconnectInput - where: MovieActorsConnectionWhere - } - - input MovieActorsFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - } - - input MovieActorsNodeAggregationWhereInput { - AND: [MovieActorsNodeAggregationWhereInput!] - NOT: MovieActorsNodeAggregationWhereInput - OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type MovieActorsRelationship { - cursor: String! - node: Actor! - properties: ActedIn! - } - - input MovieActorsUpdateConnectionInput { - edge: ActedInUpdateInput - node: ActorUpdateInput - } - - input MovieActorsUpdateFieldInput { - connect: [MovieActorsConnectFieldInput!] - create: [MovieActorsCreateFieldInput!] - delete: [MovieActorsDeleteFieldInput!] - disconnect: [MovieActorsDisconnectFieldInput!] - update: MovieActorsUpdateConnectionInput - where: MovieActorsConnectionWhere - } - - type MovieAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - input MovieConnectInput { - actors: [MovieActorsConnectFieldInput!] - } - - input MovieConnectWhere { - node: MovieWhere! - } - - input MovieCreateInput { - actors: MovieActorsFieldInput - title: String! - } - - input MovieDeleteInput { - actors: [MovieActorsDeleteFieldInput!] - } - - input MovieDisconnectInput { - actors: [MovieActorsDisconnectFieldInput!] - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - title: SortDirection - } - - input MovieUpdateInput { - actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - actorsAggregate: MovieActorsAggregateInput - \\"\\"\\" - Return Movies where all of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where none of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where one of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere - \\"\\"\\" - Return Movies where some of the related MovieActorsConnections match this filter - \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere - \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere - \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere - \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere - \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createActors(input: [ActorCreateInput!]!): CreateActorsMutationResponse! - createGenres(input: [GenreCreateInput!]!): CreateGenresMutationResponse! - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - deleteActors(delete: ActorDeleteInput, where: ActorWhere): DeleteInfo! - deleteGenres(where: GenreWhere): DeleteInfo! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - updateActors(update: ActorUpdateInput, where: ActorWhere): UpdateActorsMutationResponse! - updateGenres(update: GenreUpdateInput, where: GenreWhere): UpdateGenresMutationResponse! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - interface Production { - title: String! - } - - type ProductionAggregateSelection { - count: Int! - title: StringAggregateSelection! - } - - type ProductionEdge { - cursor: String! - node: Production! - } - - enum ProductionImplementation { - Movie - } - - \\"\\"\\" - Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. - \\"\\"\\" - input ProductionSort { - title: SortDirection - } - - input ProductionWhere { - AND: [ProductionWhere!] - NOT: ProductionWhere - OR: [ProductionWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type ProductionsConnection { - edges: [ProductionEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(where: ActorWhere): ActorAggregateSelection! - actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! - genresAggregate(where: GenreWhere): GenreAggregateSelection! - genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, sort: [ProductionSort!], where: ProductionWhere): [Production!]! - productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! - productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - searches(limit: Int, offset: Int, where: SearchWhere): [Search!]! - } - - union Search = Genre | Movie - - input SearchWhere { - Genre: GenreWhere - Movie: MovieWhere - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type UpdateActorsMutationResponse { - actors: [Actor!]! - info: UpdateInfo! - } - - type UpdateGenresMutationResponse { - genres: [Genre!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/query-direction.test.ts b/packages/graphql/tests/schema/remove-deprecated/query-direction.test.ts deleted file mode 100644 index 978517857d..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/query-direction.test.ts +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { gql } from "graphql-tag"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("Query Direction", () => { - test("DEFAULT_UNDIRECTED", async () => { - const typeDefs = gql` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - friends(directed: Boolean = false @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - friendsAggregate(directed: Boolean = false @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): UserUserFriendsAggregationSelection - friendsConnection(after: String, directed: Boolean = false @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [UserFriendsConnectionSort!], where: UserFriendsConnectionWhere): UserFriendsConnection! - name: String! - } - - type UserAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input UserConnectInput { - friends: [UserFriendsConnectFieldInput!] - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - friends: UserFriendsFieldInput - name: String! - } - - input UserDeleteInput { - friends: [UserFriendsDeleteFieldInput!] - } - - input UserDisconnectInput { - friends: [UserFriendsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserFriendsAggregateInput { - AND: [UserFriendsAggregateInput!] - NOT: UserFriendsAggregateInput - OR: [UserFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserFriendsNodeAggregationWhereInput - } - - input UserFriendsConnectFieldInput { - connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type UserFriendsConnection { - edges: [UserFriendsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserFriendsConnectionSort { - node: UserSort - } - - input UserFriendsConnectionWhere { - AND: [UserFriendsConnectionWhere!] - NOT: UserFriendsConnectionWhere - OR: [UserFriendsConnectionWhere!] - node: UserWhere - } - - input UserFriendsCreateFieldInput { - node: UserCreateInput! - } - - input UserFriendsDeleteFieldInput { - delete: UserDeleteInput - where: UserFriendsConnectionWhere - } - - input UserFriendsDisconnectFieldInput { - disconnect: UserDisconnectInput - where: UserFriendsConnectionWhere - } - - input UserFriendsFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - } - - input UserFriendsNodeAggregationWhereInput { - AND: [UserFriendsNodeAggregationWhereInput!] - NOT: UserFriendsNodeAggregationWhereInput - OR: [UserFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type UserFriendsRelationship { - cursor: String! - node: User! - } - - input UserFriendsUpdateConnectionInput { - node: UserUpdateInput - } - - input UserFriendsUpdateFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - delete: [UserFriendsDeleteFieldInput!] - disconnect: [UserFriendsDisconnectFieldInput!] - update: UserFriendsUpdateConnectionInput - where: UserFriendsConnectionWhere - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - name: SortDirection - } - - input UserUpdateInput { - friends: [UserFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type UserUserFriendsAggregationSelection { - count: Int! - node: UserUserFriendsNodeAggregateSelection - } - - type UserUserFriendsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - friendsAggregate: UserFriendsAggregateInput - \\"\\"\\" - Return Users where all of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_ALL: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_NONE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SINGLE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SOME: UserFriendsConnectionWhere - \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - friends_ALL: UserWhere - \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - friends_NONE: UserWhere - \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - friends_SINGLE: UserWhere - \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - friends_SOME: UserWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); - - test("DIRECTED_ONLY", async () => { - const typeDefs = gql` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - friends(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - friendsAggregate(where: UserWhere): UserUserFriendsAggregationSelection - friendsConnection(after: String, first: Int, sort: [UserFriendsConnectionSort!], where: UserFriendsConnectionWhere): UserFriendsConnection! - name: String! - } - - type UserAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input UserConnectInput { - friends: [UserFriendsConnectFieldInput!] - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - friends: UserFriendsFieldInput - name: String! - } - - input UserDeleteInput { - friends: [UserFriendsDeleteFieldInput!] - } - - input UserDisconnectInput { - friends: [UserFriendsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserFriendsAggregateInput { - AND: [UserFriendsAggregateInput!] - NOT: UserFriendsAggregateInput - OR: [UserFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserFriendsNodeAggregationWhereInput - } - - input UserFriendsConnectFieldInput { - connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type UserFriendsConnection { - edges: [UserFriendsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserFriendsConnectionSort { - node: UserSort - } - - input UserFriendsConnectionWhere { - AND: [UserFriendsConnectionWhere!] - NOT: UserFriendsConnectionWhere - OR: [UserFriendsConnectionWhere!] - node: UserWhere - } - - input UserFriendsCreateFieldInput { - node: UserCreateInput! - } - - input UserFriendsDeleteFieldInput { - delete: UserDeleteInput - where: UserFriendsConnectionWhere - } - - input UserFriendsDisconnectFieldInput { - disconnect: UserDisconnectInput - where: UserFriendsConnectionWhere - } - - input UserFriendsFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - } - - input UserFriendsNodeAggregationWhereInput { - AND: [UserFriendsNodeAggregationWhereInput!] - NOT: UserFriendsNodeAggregationWhereInput - OR: [UserFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type UserFriendsRelationship { - cursor: String! - node: User! - } - - input UserFriendsUpdateConnectionInput { - node: UserUpdateInput - } - - input UserFriendsUpdateFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - delete: [UserFriendsDeleteFieldInput!] - disconnect: [UserFriendsDisconnectFieldInput!] - update: UserFriendsUpdateConnectionInput - where: UserFriendsConnectionWhere - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - name: SortDirection - } - - input UserUpdateInput { - friends: [UserFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type UserUserFriendsAggregationSelection { - count: Int! - node: UserUserFriendsNodeAggregateSelection - } - - type UserUserFriendsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - friendsAggregate: UserFriendsAggregateInput - \\"\\"\\" - Return Users where all of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_ALL: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_NONE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SINGLE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SOME: UserFriendsConnectionWhere - \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - friends_ALL: UserWhere - \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - friends_NONE: UserWhere - \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - friends_SINGLE: UserWhere - \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - friends_SOME: UserWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); - - test("UNDIRECTED_ONLY", async () => { - const typeDefs = gql` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - const neoSchema = new Neo4jGraphQL({ typeDefs }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateUsersMutationResponse { - info: CreateInfo! - users: [User!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type Mutation { - createUsers(input: [UserCreateInput!]!): CreateUsersMutationResponse! - deleteUsers(delete: UserDeleteInput, where: UserWhere): DeleteInfo! - updateUsers(update: UserUpdateInput, where: UserWhere): UpdateUsersMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateUsersMutationResponse { - info: UpdateInfo! - users: [User!]! - } - - type User { - friends(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - friendsAggregate(where: UserWhere): UserUserFriendsAggregationSelection - friendsConnection(after: String, first: Int, sort: [UserFriendsConnectionSort!], where: UserFriendsConnectionWhere): UserFriendsConnection! - name: String! - } - - type UserAggregateSelection { - count: Int! - name: StringAggregateSelection! - } - - input UserConnectInput { - friends: [UserFriendsConnectFieldInput!] - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - friends: UserFriendsFieldInput - name: String! - } - - input UserDeleteInput { - friends: [UserFriendsDeleteFieldInput!] - } - - input UserDisconnectInput { - friends: [UserFriendsDisconnectFieldInput!] - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserFriendsAggregateInput { - AND: [UserFriendsAggregateInput!] - NOT: UserFriendsAggregateInput - OR: [UserFriendsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: UserFriendsNodeAggregationWhereInput - } - - input UserFriendsConnectFieldInput { - connect: [UserConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type UserFriendsConnection { - edges: [UserFriendsRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input UserFriendsConnectionSort { - node: UserSort - } - - input UserFriendsConnectionWhere { - AND: [UserFriendsConnectionWhere!] - NOT: UserFriendsConnectionWhere - OR: [UserFriendsConnectionWhere!] - node: UserWhere - } - - input UserFriendsCreateFieldInput { - node: UserCreateInput! - } - - input UserFriendsDeleteFieldInput { - delete: UserDeleteInput - where: UserFriendsConnectionWhere - } - - input UserFriendsDisconnectFieldInput { - disconnect: UserDisconnectInput - where: UserFriendsConnectionWhere - } - - input UserFriendsFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - } - - input UserFriendsNodeAggregationWhereInput { - AND: [UserFriendsNodeAggregationWhereInput!] - NOT: UserFriendsNodeAggregationWhereInput - OR: [UserFriendsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - } - - type UserFriendsRelationship { - cursor: String! - node: User! - } - - input UserFriendsUpdateConnectionInput { - node: UserUpdateInput - } - - input UserFriendsUpdateFieldInput { - connect: [UserFriendsConnectFieldInput!] - create: [UserFriendsCreateFieldInput!] - delete: [UserFriendsDeleteFieldInput!] - disconnect: [UserFriendsDisconnectFieldInput!] - update: UserFriendsUpdateConnectionInput - where: UserFriendsConnectionWhere - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - name: SortDirection - } - - input UserUpdateInput { - friends: [UserFriendsUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - } - - type UserUserFriendsAggregationSelection { - count: Int! - node: UserUserFriendsNodeAggregateSelection - } - - type UserUserFriendsNodeAggregateSelection { - name: StringAggregateSelection! - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - friendsAggregate: UserFriendsAggregateInput - \\"\\"\\" - Return Users where all of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_ALL: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where none of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_NONE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where one of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SINGLE: UserFriendsConnectionWhere - \\"\\"\\" - Return Users where some of the related UserFriendsConnections match this filter - \\"\\"\\" - friendsConnection_SOME: UserFriendsConnectionWhere - \\"\\"\\"Return Users where all of the related Users match this filter\\"\\"\\" - friends_ALL: UserWhere - \\"\\"\\"Return Users where none of the related Users match this filter\\"\\"\\" - friends_NONE: UserWhere - \\"\\"\\"Return Users where one of the related Users match this filter\\"\\"\\" - friends_SINGLE: UserWhere - \\"\\"\\"Return Users where some of the related Users match this filter\\"\\"\\" - friends_SOME: UserWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/remove-deprecated/relationship-filtering.test.ts b/packages/graphql/tests/schema/remove-deprecated/relationship-filtering.test.ts new file mode 100644 index 0000000000..0c054a6c3f --- /dev/null +++ b/packages/graphql/tests/schema/remove-deprecated/relationship-filtering.test.ts @@ -0,0 +1,1564 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { printSchemaWithDirectives } from "@graphql-tools/utils"; +import { lexicographicSortSchema } from "graphql/utilities"; +import { Neo4jGraphQL } from "../../../src"; + +describe("Exclude suffix based filtering", () => { + test("should exclude suffix based filtering", async () => { + const typeDefs = /* GraphQL */ ` + type typeA @node { + name: String + actedIn: [typeB!]! @relationship(type: "HAS_TYPE", properties: "relType", direction: OUT) + } + + type typeB implements interfaceC @node { + id: ID! + ratings: [Float!]! + rels: [typeA!]! @relationship(type: "HAS_TYPE", properties: "relType", direction: IN) + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + list: [String!]! + } + union d = typeA | typeB + + interface interfaceC { + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + list: [String!]! + } + + type relType @relationshipProperties { + pay: [Float!] + averageRating: Float! + date: Date + duration: Duration + localDateTime: LocalDateTime + createdAt: DateTime + cartesianPoint: CartesianPoint + point: Point + time: Time + localTime: LocalTime + } + `; + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { + excludeDeprecatedFields: { + relationshipFilters: true, + }, + }, + }); + const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); + + expect(printedSchema).toMatchInlineSnapshot(` + "schema { + query: Query + mutation: Mutation + } + + \\"\\"\\"Distance filters for cartesian points\\"\\"\\" + input CartesianDistancePointFilters { + from: CartesianPointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\" + A point in a two- or three-dimensional Cartesian coordinate system or in a three-dimensional cylindrical coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#cartesian-point + \\"\\"\\" + type CartesianPoint { + crs: String! + srid: Int! + x: Float! + y: Float! + z: Float + } + + \\"\\"\\"Input type for a cartesian point with a distance\\"\\"\\" + input CartesianPointDistance { + distance: Float! + point: CartesianPointInput! + } + + \\"\\"\\"Cartesian Point filters\\"\\"\\" + input CartesianPointFilters { + distance: CartesianDistancePointFilters + eq: CartesianPointInput + in: [CartesianPointInput!] + } + + \\"\\"\\"Input type for a cartesian point\\"\\"\\" + input CartesianPointInput { + x: Float! + y: Float! + z: Float + } + + \\"\\"\\"CartesianPoint mutations\\"\\"\\" + input CartesianPointMutations { + set: CartesianPointInput + } + + \\"\\"\\" + Information about the number of nodes and relationships created during a create mutation + \\"\\"\\" + type CreateInfo { + nodesCreated: Int! + relationshipsCreated: Int! + } + + type CreateTypeASMutationResponse { + info: CreateInfo! + typeAS: [typeA!]! + } + + type CreateTypeBSMutationResponse { + info: CreateInfo! + typeBS: [typeB!]! + } + + \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" + scalar Date + + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + + \\"\\"\\"Date mutations\\"\\"\\" + input DateScalarMutations { + set: Date + } + + \\"\\"\\"A date and time, represented as an ISO-8601 string\\"\\"\\" + scalar DateTime + + type DateTimeAggregateSelection { + max: DateTime + min: DateTime + } + + \\"\\"\\"Filters for an aggregation of an DateTime input field\\"\\"\\" + input DateTimeScalarAggregationFilters { + max: DateTimeScalarFilters + min: DateTimeScalarFilters + } + + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + + \\"\\"\\" + Information about the number of nodes and relationships deleted during a delete mutation + \\"\\"\\" + type DeleteInfo { + nodesDeleted: Int! + relationshipsDeleted: Int! + } + + \\"\\"\\"A duration, represented as an ISO 8601 duration string\\"\\"\\" + scalar Duration + + type DurationAggregateSelection { + max: Duration + min: Duration + } + + \\"\\"\\"Filters for an aggregation of a Dutation input field\\"\\"\\" + input DurationScalarAggregationFilters { + average: DurationScalarFilters + max: DurationScalarFilters + min: DurationScalarFilters + } + + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + type FloatAggregateSelection { + average: Float + max: Float + min: Float + sum: Float + } + + \\"\\"\\"Float list filters\\"\\"\\" + input FloatListFilters { + eq: [Float!] + includes: Float + } + + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + type InterfaceCSConnection { + edges: [interfaceCEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + \\"\\"\\"Mutations for a list for Float\\"\\"\\" + input ListFloatMutations { + pop: Int + push: [Float!] + set: [Float!] + } + + \\"\\"\\"Mutations for a list for String\\"\\"\\" + input ListStringMutations { + pop: Int + push: [String!] + set: [String!] + } + + \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" + scalar LocalDateTime + + type LocalDateTimeAggregateSelection { + max: LocalDateTime + min: LocalDateTime + } + + \\"\\"\\"Filters for an aggregation of an LocalDateTime input field\\"\\"\\" + input LocalDateTimeScalarAggregationFilters { + max: LocalDateTimeScalarFilters + min: LocalDateTimeScalarFilters + } + + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + + \\"\\"\\" + A local time, represented as a time string without timezone information + \\"\\"\\" + scalar LocalTime + + type LocalTimeAggregateSelection { + max: LocalTime + min: LocalTime + } + + \\"\\"\\"Filters for an aggregation of an LocalTime input field\\"\\"\\" + input LocalTimeScalarAggregationFilters { + max: LocalTimeScalarFilters + min: LocalTimeScalarFilters + } + + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + + type Mutation { + createTypeAS(input: [typeACreateInput!]!): CreateTypeASMutationResponse! + createTypeBS(input: [typeBCreateInput!]!): CreateTypeBSMutationResponse! + deleteTypeAS(delete: typeADeleteInput, where: typeAWhere): DeleteInfo! + deleteTypeBS(delete: typeBDeleteInput, where: typeBWhere): DeleteInfo! + updateTypeAS(update: typeAUpdateInput, where: typeAWhere): UpdateTypeASMutationResponse! + updateTypeBS(update: typeBUpdateInput, where: typeBWhere): UpdateTypeBSMutationResponse! + } + + \\"\\"\\"Pagination information (Relay)\\"\\"\\" + type PageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + } + + \\"\\"\\" + A point in a coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#point + \\"\\"\\" + type Point { + crs: String! + height: Float + latitude: Float! + longitude: Float! + srid: Int! + } + + \\"\\"\\"Input type for a point with a distance\\"\\"\\" + input PointDistance { + \\"\\"\\"The distance in metres to be used when comparing two points\\"\\"\\" + distance: Float! + point: PointInput! + } + + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + + \\"\\"\\"Input type for a point\\"\\"\\" + input PointInput { + height: Float + latitude: Float! + longitude: Float! + } + + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + + type Query { + ds(limit: Int, offset: Int, where: dWhere): [d!]! + interfaceCS(limit: Int, offset: Int, sort: [interfaceCSort!], where: interfaceCWhere): [interfaceC!]! + interfaceCSAggregate(where: interfaceCWhere): interfaceCAggregateSelection! + interfaceCSConnection(after: String, first: Int, sort: [interfaceCSort!], where: interfaceCWhere): InterfaceCSConnection! + typeAS(limit: Int, offset: Int, sort: [typeASort!], where: typeAWhere): [typeA!]! + typeASAggregate(where: typeAWhere): typeAAggregateSelection! + typeASConnection(after: String, first: Int, sort: [typeASort!], where: typeAWhere): TypeASConnection! + typeBS(limit: Int, offset: Int, sort: [typeBSort!], where: typeBWhere): [typeB!]! + typeBSAggregate(where: typeBWhere): typeBAggregateSelection! + typeBSConnection(after: String, first: Int, sort: [typeBSort!], where: typeBWhere): TypeBSConnection! + } + + \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" + enum SortDirection { + \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" + ASC + \\"\\"\\"Sort by field values in descending order.\\"\\"\\" + DESC + } + + type StringAggregateSelection { + longest: String + shortest: String + } + + \\"\\"\\"String list filters\\"\\"\\" + input StringListFilters { + eq: [String!] + includes: String + } + + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + + \\"\\"\\"A time, represented as an RFC3339 time string\\"\\"\\" + scalar Time + + type TimeAggregateSelection { + max: Time + min: Time + } + + \\"\\"\\"Filters for an aggregation of an Time input field\\"\\"\\" + input TimeScalarAggregationFilters { + max: TimeScalarFilters + min: TimeScalarFilters + } + + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + + type TypeASConnection { + edges: [typeAEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + type TypeBSConnection { + edges: [typeBEdge!]! + pageInfo: PageInfo! + totalCount: Int! + } + + \\"\\"\\" + Information about the number of nodes and relationships created and deleted during an update mutation + \\"\\"\\" + type UpdateInfo { + nodesCreated: Int! + nodesDeleted: Int! + relationshipsCreated: Int! + relationshipsDeleted: Int! + } + + type UpdateTypeASMutationResponse { + info: UpdateInfo! + typeAS: [typeA!]! + } + + type UpdateTypeBSMutationResponse { + info: UpdateInfo! + typeBS: [typeB!]! + } + + union d = typeA | typeB + + input dWhere { + typeA: typeAWhere + typeB: typeBWhere + } + + interface interfaceC { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: Point + time: Time + } + + type interfaceCAggregateSelection { + averageRating: FloatAggregateSelection! + count: Int! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type interfaceCEdge { + cursor: String! + node: interfaceC! + } + + enum interfaceCImplementation { + typeB + } + + \\"\\"\\" + Fields to sort InterfaceCS by. The order in which sorts are applied is not guaranteed when specifying many fields in one interfaceCSort object. + \\"\\"\\" + input interfaceCSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + localDateTime: SortDirection + localTime: SortDirection + point: SortDirection + time: SortDirection + } + + input interfaceCWhere { + AND: [interfaceCWhere!] + NOT: interfaceCWhere + OR: [interfaceCWhere!] + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + cartesianPoint: CartesianPointFilters + cartesianPoint_DISTANCE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { distance: ... }\\") + cartesianPoint_EQ: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { eq: ... }\\") + cartesianPoint_GT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gt: ... }\\") + cartesianPoint_GTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gte: ... }\\") + cartesianPoint_IN: [CartesianPointInput] @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { in: ... }\\") + cartesianPoint_LT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lt: ... }\\") + cartesianPoint_LTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lte: ... }\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + date: DateScalarFilters + date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter date: { eq: ... }\\") + date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gt: ... }\\") + date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gte: ... }\\") + date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter date: { in: ... }\\") + date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lt: ... }\\") + date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lte: ... }\\") + duration: DurationScalarFilters + duration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { eq: ... }\\") + duration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gt: ... }\\") + duration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gte: ... }\\") + duration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter duration: { in: ... }\\") + duration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lt: ... }\\") + duration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lte: ... }\\") + list: StringListFilters + list_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter list: { eq: ... }\\") + list_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter list: { includes: ... }\\") + localDateTime: LocalDateTimeScalarFilters + localDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { eq: ... }\\") + localDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gt: ... }\\") + localDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gte: ... }\\") + localDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { in: ... }\\") + localDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lt: ... }\\") + localDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lte: ... }\\") + localTime: LocalTimeScalarFilters + localTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { eq: ... }\\") + localTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gt: ... }\\") + localTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gte: ... }\\") + localTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter localTime: { in: ... }\\") + localTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lt: ... }\\") + localTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lte: ... }\\") + point: PointFilters + point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { distance: ... }\\") + point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter point: { eq: ... }\\") + point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gt: ... }\\") + point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gte: ... }\\") + point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter point: { in: ... }\\") + point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lt: ... }\\") + point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lte: ... }\\") + time: TimeScalarFilters + time_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter time: { eq: ... }\\") + time_GT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gt: ... }\\") + time_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gte: ... }\\") + time_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter time: { in: ... }\\") + time_LT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lt: ... }\\") + time_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lte: ... }\\") + typename: [interfaceCImplementation!] + } + + \\"\\"\\" + The edge properties for the following fields: + * typeA.actedIn + * typeB.rels + \\"\\"\\" + type relType { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + localDateTime: LocalDateTime + localTime: LocalTime + pay: [Float!] + point: Point + time: Time + } + + input relTypeAggregationWhereInput { + AND: [relTypeAggregationWhereInput!] + NOT: relTypeAggregationWhereInput + OR: [relTypeAggregationWhereInput!] + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + duration: DurationScalarAggregationFilters + duration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { eq: ... } } }' instead.\\") + duration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gt: ... } } }' instead.\\") + duration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gte: ... } } }' instead.\\") + duration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lt: ... } } }' instead.\\") + duration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lte: ... } } }' instead.\\") + duration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { eq: ... } } }' instead.\\") + duration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gt: ... } } }' instead.\\") + duration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gte: ... } } }' instead.\\") + duration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lt: ... } } }' instead.\\") + duration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lte: ... } } }' instead.\\") + duration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { eq: ... } } }' instead.\\") + duration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gt: ... } } }' instead.\\") + duration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gte: ... } } }' instead.\\") + duration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lt: ... } } }' instead.\\") + duration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lte: ... } } }' instead.\\") + localDateTime: LocalDateTimeScalarAggregationFilters + localDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { eq: ... } } }' instead.\\") + localDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gt: ... } } }' instead.\\") + localDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gte: ... } } }' instead.\\") + localDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lt: ... } } }' instead.\\") + localDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lte: ... } } }' instead.\\") + localDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { eq: ... } } }' instead.\\") + localDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gt: ... } } }' instead.\\") + localDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gte: ... } } }' instead.\\") + localDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lt: ... } } }' instead.\\") + localDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lte: ... } } }' instead.\\") + localTime: LocalTimeScalarAggregationFilters + localTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { eq: ... } } }' instead.\\") + localTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gt: ... } } }' instead.\\") + localTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gte: ... } } }' instead.\\") + localTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lt: ... } } }' instead.\\") + localTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lte: ... } } }' instead.\\") + localTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { eq: ... } } }' instead.\\") + localTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gt: ... } } }' instead.\\") + localTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gte: ... } } }' instead.\\") + localTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lt: ... } } }' instead.\\") + localTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lte: ... } } }' instead.\\") + time: TimeScalarAggregationFilters + time_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { eq: ... } } }' instead.\\") + time_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gt: ... } } }' instead.\\") + time_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gte: ... } } }' instead.\\") + time_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lt: ... } } }' instead.\\") + time_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lte: ... } } }' instead.\\") + time_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { eq: ... } } }' instead.\\") + time_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gt: ... } } }' instead.\\") + time_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gte: ... } } }' instead.\\") + time_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lt: ... } } }' instead.\\") + time_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lte: ... } } }' instead.\\") + } + + input relTypeCreateInput { + averageRating: Float! + cartesianPoint: CartesianPointInput + createdAt: DateTime + date: Date + duration: Duration + localDateTime: LocalDateTime + localTime: LocalTime + pay: [Float!] + point: PointInput + time: Time + } + + input relTypeSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + localDateTime: SortDirection + localTime: SortDirection + pay: SortDirection + point: SortDirection + time: SortDirection + } + + input relTypeUpdateInput { + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + cartesianPoint: CartesianPointMutations + cartesianPoint_SET: CartesianPointInput @deprecated(reason: \\"Please use the generic mutation 'cartesianPoint: { set: ... } }' instead.\\") + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + date: DateScalarMutations + date_SET: Date @deprecated(reason: \\"Please use the generic mutation 'date: { set: ... } }' instead.\\") + duration: DurationScalarMutations + duration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'duration: { set: ... } }' instead.\\") + localDateTime: LocalDateTimeScalarMutations + localDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'localDateTime: { set: ... } }' instead.\\") + localTime: LocalTimeScalarMutations + localTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'localTime: { set: ... } }' instead.\\") + pay: ListFloatMutations + pay_POP: Int @deprecated(reason: \\"Please use the generic mutation 'pay: { pop: ... } }' instead.\\") + pay_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { push: ... } }' instead.\\") + pay_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'pay: { set: ... } }' instead.\\") + point: PointMutations + point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'point: { set: ... } }' instead.\\") + time: TimeScalarMutations + time_SET: Time @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") + } + + input relTypeWhere { + AND: [relTypeWhere!] + NOT: relTypeWhere + OR: [relTypeWhere!] + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + cartesianPoint: CartesianPointFilters + cartesianPoint_DISTANCE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { distance: ... }\\") + cartesianPoint_EQ: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { eq: ... }\\") + cartesianPoint_GT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gt: ... }\\") + cartesianPoint_GTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gte: ... }\\") + cartesianPoint_IN: [CartesianPointInput] @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { in: ... }\\") + cartesianPoint_LT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lt: ... }\\") + cartesianPoint_LTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lte: ... }\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + date: DateScalarFilters + date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter date: { eq: ... }\\") + date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gt: ... }\\") + date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gte: ... }\\") + date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter date: { in: ... }\\") + date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lt: ... }\\") + date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lte: ... }\\") + duration: DurationScalarFilters + duration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { eq: ... }\\") + duration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gt: ... }\\") + duration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gte: ... }\\") + duration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter duration: { in: ... }\\") + duration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lt: ... }\\") + duration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lte: ... }\\") + localDateTime: LocalDateTimeScalarFilters + localDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { eq: ... }\\") + localDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gt: ... }\\") + localDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gte: ... }\\") + localDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { in: ... }\\") + localDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lt: ... }\\") + localDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lte: ... }\\") + localTime: LocalTimeScalarFilters + localTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { eq: ... }\\") + localTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gt: ... }\\") + localTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gte: ... }\\") + localTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter localTime: { in: ... }\\") + localTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lt: ... }\\") + localTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lte: ... }\\") + pay: FloatListFilters + pay_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter pay: { eq: ... }\\") + pay_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter pay: { includes: ... }\\") + point: PointFilters + point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { distance: ... }\\") + point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter point: { eq: ... }\\") + point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gt: ... }\\") + point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gte: ... }\\") + point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter point: { in: ... }\\") + point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lt: ... }\\") + point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lte: ... }\\") + time: TimeScalarFilters + time_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter time: { eq: ... }\\") + time_GT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gt: ... }\\") + time_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gte: ... }\\") + time_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter time: { in: ... }\\") + time_LT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lt: ... }\\") + time_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lte: ... }\\") + } + + type typeA { + actedIn(limit: Int, offset: Int, sort: [typeBSort!], where: typeBWhere): [typeB!]! + actedInAggregate(where: typeBWhere): typeAtypeBActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [typeAActedInConnectionSort!], where: typeAActedInConnectionWhere): typeAActedInConnection! + name: String + } + + input typeAActedInAggregateInput { + AND: [typeAActedInAggregateInput!] + NOT: typeAActedInAggregateInput + OR: [typeAActedInAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: relTypeAggregationWhereInput + node: typeAActedInNodeAggregationWhereInput + } + + input typeAActedInConnectFieldInput { + connect: [typeBConnectInput!] + edge: relTypeCreateInput! + where: typeBConnectWhere + } + + type typeAActedInConnection { + edges: [typeAActedInRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input typeAActedInConnectionFilters { + \\"\\"\\" + Return typeAS where all of the related typeAActedInConnections match this filter + \\"\\"\\" + all: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where none of the related typeAActedInConnections match this filter + \\"\\"\\" + none: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where one of the related typeAActedInConnections match this filter + \\"\\"\\" + single: typeAActedInConnectionWhere + \\"\\"\\" + Return typeAS where some of the related typeAActedInConnections match this filter + \\"\\"\\" + some: typeAActedInConnectionWhere + } + + input typeAActedInConnectionSort { + edge: relTypeSort + node: typeBSort + } + + input typeAActedInConnectionWhere { + AND: [typeAActedInConnectionWhere!] + NOT: typeAActedInConnectionWhere + OR: [typeAActedInConnectionWhere!] + edge: relTypeWhere + node: typeBWhere + } + + input typeAActedInCreateFieldInput { + edge: relTypeCreateInput! + node: typeBCreateInput! + } + + input typeAActedInDeleteFieldInput { + delete: typeBDeleteInput + where: typeAActedInConnectionWhere + } + + input typeAActedInDisconnectFieldInput { + disconnect: typeBDisconnectInput + where: typeAActedInConnectionWhere + } + + input typeAActedInFieldInput { + connect: [typeAActedInConnectFieldInput!] + create: [typeAActedInCreateFieldInput!] + } + + input typeAActedInNodeAggregationWhereInput { + AND: [typeAActedInNodeAggregationWhereInput!] + NOT: typeAActedInNodeAggregationWhereInput + OR: [typeAActedInNodeAggregationWhereInput!] + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") + createdAt: DateTimeScalarAggregationFilters + createdAt_MAX_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { eq: ... } } }' instead.\\") + createdAt_MAX_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gt: ... } } }' instead.\\") + createdAt_MAX_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { gte: ... } } }' instead.\\") + createdAt_MAX_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lt: ... } } }' instead.\\") + createdAt_MAX_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { max: { lte: ... } } }' instead.\\") + createdAt_MIN_EQUAL: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { eq: ... } } }' instead.\\") + createdAt_MIN_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gt: ... } } }' instead.\\") + createdAt_MIN_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { gte: ... } } }' instead.\\") + createdAt_MIN_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lt: ... } } }' instead.\\") + createdAt_MIN_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter 'createdAt: { min: { lte: ... } } }' instead.\\") + duration: DurationScalarAggregationFilters + duration_AVERAGE_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { eq: ... } } }' instead.\\") + duration_AVERAGE_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gt: ... } } }' instead.\\") + duration_AVERAGE_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { gte: ... } } }' instead.\\") + duration_AVERAGE_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lt: ... } } }' instead.\\") + duration_AVERAGE_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { average: { lte: ... } } }' instead.\\") + duration_MAX_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { eq: ... } } }' instead.\\") + duration_MAX_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gt: ... } } }' instead.\\") + duration_MAX_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { gte: ... } } }' instead.\\") + duration_MAX_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lt: ... } } }' instead.\\") + duration_MAX_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { max: { lte: ... } } }' instead.\\") + duration_MIN_EQUAL: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { eq: ... } } }' instead.\\") + duration_MIN_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gt: ... } } }' instead.\\") + duration_MIN_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { gte: ... } } }' instead.\\") + duration_MIN_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lt: ... } } }' instead.\\") + duration_MIN_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter 'duration: { min: { lte: ... } } }' instead.\\") + localDateTime: LocalDateTimeScalarAggregationFilters + localDateTime_MAX_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { eq: ... } } }' instead.\\") + localDateTime_MAX_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gt: ... } } }' instead.\\") + localDateTime_MAX_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { gte: ... } } }' instead.\\") + localDateTime_MAX_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lt: ... } } }' instead.\\") + localDateTime_MAX_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { max: { lte: ... } } }' instead.\\") + localDateTime_MIN_EQUAL: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { eq: ... } } }' instead.\\") + localDateTime_MIN_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gt: ... } } }' instead.\\") + localDateTime_MIN_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { gte: ... } } }' instead.\\") + localDateTime_MIN_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lt: ... } } }' instead.\\") + localDateTime_MIN_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter 'localDateTime: { min: { lte: ... } } }' instead.\\") + localTime: LocalTimeScalarAggregationFilters + localTime_MAX_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { eq: ... } } }' instead.\\") + localTime_MAX_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gt: ... } } }' instead.\\") + localTime_MAX_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { gte: ... } } }' instead.\\") + localTime_MAX_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lt: ... } } }' instead.\\") + localTime_MAX_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { max: { lte: ... } } }' instead.\\") + localTime_MIN_EQUAL: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { eq: ... } } }' instead.\\") + localTime_MIN_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gt: ... } } }' instead.\\") + localTime_MIN_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { gte: ... } } }' instead.\\") + localTime_MIN_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lt: ... } } }' instead.\\") + localTime_MIN_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter 'localTime: { min: { lte: ... } } }' instead.\\") + time: TimeScalarAggregationFilters + time_MAX_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { eq: ... } } }' instead.\\") + time_MAX_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gt: ... } } }' instead.\\") + time_MAX_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { gte: ... } } }' instead.\\") + time_MAX_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lt: ... } } }' instead.\\") + time_MAX_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { max: { lte: ... } } }' instead.\\") + time_MIN_EQUAL: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { eq: ... } } }' instead.\\") + time_MIN_GT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gt: ... } } }' instead.\\") + time_MIN_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { gte: ... } } }' instead.\\") + time_MIN_LT: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lt: ... } } }' instead.\\") + time_MIN_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter 'time: { min: { lte: ... } } }' instead.\\") + } + + type typeAActedInRelationship { + cursor: String! + node: typeB! + properties: relType! + } + + input typeAActedInUpdateConnectionInput { + edge: relTypeUpdateInput + node: typeBUpdateInput + } + + input typeAActedInUpdateFieldInput { + connect: [typeAActedInConnectFieldInput!] + create: [typeAActedInCreateFieldInput!] + delete: [typeAActedInDeleteFieldInput!] + disconnect: [typeAActedInDisconnectFieldInput!] + update: typeAActedInUpdateConnectionInput + where: typeAActedInConnectionWhere + } + + type typeAAggregateSelection { + count: Int! + name: StringAggregateSelection! + } + + input typeAConnectInput { + actedIn: [typeAActedInConnectFieldInput!] + } + + input typeAConnectWhere { + node: typeAWhere! + } + + input typeACreateInput { + actedIn: typeAActedInFieldInput + name: String + } + + input typeADeleteInput { + actedIn: [typeAActedInDeleteFieldInput!] + } + + input typeADisconnectInput { + actedIn: [typeAActedInDisconnectFieldInput!] + } + + type typeAEdge { + cursor: String! + node: typeA! + } + + input typeARelationshipFilters { + \\"\\"\\"Filter type where all of the related typeAS match this filter\\"\\"\\" + all: typeAWhere + \\"\\"\\"Filter type where none of the related typeAS match this filter\\"\\"\\" + none: typeAWhere + \\"\\"\\"Filter type where one of the related typeAS match this filter\\"\\"\\" + single: typeAWhere + \\"\\"\\"Filter type where some of the related typeAS match this filter\\"\\"\\" + some: typeAWhere + } + + \\"\\"\\" + Fields to sort TypeAS by. The order in which sorts are applied is not guaranteed when specifying many fields in one typeASort object. + \\"\\"\\" + input typeASort { + name: SortDirection + } + + input typeAUpdateInput { + actedIn: [typeAActedInUpdateFieldInput!] + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + } + + input typeAWhere { + AND: [typeAWhere!] + NOT: typeAWhere + OR: [typeAWhere!] + actedIn: typeBRelationshipFilters + actedInAggregate: typeAActedInAggregateInput + actedInConnection: typeAActedInConnectionFilters + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + } + + type typeAtypeBActedInAggregationSelection { + count: Int! + edge: typeAtypeBActedInEdgeAggregateSelection + node: typeAtypeBActedInNodeAggregateSelection + } + + type typeAtypeBActedInEdgeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeAtypeBActedInNodeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeB implements interfaceC { + averageRating: Float! + cartesianPoint: CartesianPoint + createdAt: DateTime + date: Date + duration: Duration + id: ID! + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: Point + ratings: [Float!]! + rels(limit: Int, offset: Int, sort: [typeASort!], where: typeAWhere): [typeA!]! + relsAggregate(where: typeAWhere): typeBtypeARelsAggregationSelection + relsConnection(after: String, first: Int, sort: [typeBRelsConnectionSort!], where: typeBRelsConnectionWhere): typeBRelsConnection! + time: Time + } + + type typeBAggregateSelection { + averageRating: FloatAggregateSelection! + count: Int! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + input typeBConnectInput { + rels: [typeBRelsConnectFieldInput!] + } + + input typeBConnectWhere { + node: typeBWhere! + } + + input typeBCreateInput { + averageRating: Float! + cartesianPoint: CartesianPointInput + createdAt: DateTime + date: Date + duration: Duration + id: ID! + list: [String!]! + localDateTime: LocalDateTime + localTime: LocalTime + point: PointInput + ratings: [Float!]! + rels: typeBRelsFieldInput + time: Time + } + + input typeBDeleteInput { + rels: [typeBRelsDeleteFieldInput!] + } + + input typeBDisconnectInput { + rels: [typeBRelsDisconnectFieldInput!] + } + + type typeBEdge { + cursor: String! + node: typeB! + } + + input typeBRelationshipFilters { + \\"\\"\\"Filter type where all of the related typeBS match this filter\\"\\"\\" + all: typeBWhere + \\"\\"\\"Filter type where none of the related typeBS match this filter\\"\\"\\" + none: typeBWhere + \\"\\"\\"Filter type where one of the related typeBS match this filter\\"\\"\\" + single: typeBWhere + \\"\\"\\"Filter type where some of the related typeBS match this filter\\"\\"\\" + some: typeBWhere + } + + input typeBRelsAggregateInput { + AND: [typeBRelsAggregateInput!] + NOT: typeBRelsAggregateInput + OR: [typeBRelsAggregateInput!] + count: IntScalarFilters + count_EQ: Int + count_GT: Int + count_GTE: Int + count_LT: Int + count_LTE: Int + edge: relTypeAggregationWhereInput + node: typeBRelsNodeAggregationWhereInput + } + + input typeBRelsConnectFieldInput { + connect: [typeAConnectInput!] + edge: relTypeCreateInput! + where: typeAConnectWhere + } + + type typeBRelsConnection { + edges: [typeBRelsRelationship!]! + pageInfo: PageInfo! + totalCount: Int! + } + + input typeBRelsConnectionFilters { + \\"\\"\\" + Return typeBS where all of the related typeBRelsConnections match this filter + \\"\\"\\" + all: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where none of the related typeBRelsConnections match this filter + \\"\\"\\" + none: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where one of the related typeBRelsConnections match this filter + \\"\\"\\" + single: typeBRelsConnectionWhere + \\"\\"\\" + Return typeBS where some of the related typeBRelsConnections match this filter + \\"\\"\\" + some: typeBRelsConnectionWhere + } + + input typeBRelsConnectionSort { + edge: relTypeSort + node: typeASort + } + + input typeBRelsConnectionWhere { + AND: [typeBRelsConnectionWhere!] + NOT: typeBRelsConnectionWhere + OR: [typeBRelsConnectionWhere!] + edge: relTypeWhere + node: typeAWhere + } + + input typeBRelsCreateFieldInput { + edge: relTypeCreateInput! + node: typeACreateInput! + } + + input typeBRelsDeleteFieldInput { + delete: typeADeleteInput + where: typeBRelsConnectionWhere + } + + input typeBRelsDisconnectFieldInput { + disconnect: typeADisconnectInput + where: typeBRelsConnectionWhere + } + + input typeBRelsFieldInput { + connect: [typeBRelsConnectFieldInput!] + create: [typeBRelsCreateFieldInput!] + } + + input typeBRelsNodeAggregationWhereInput { + AND: [typeBRelsNodeAggregationWhereInput!] + NOT: typeBRelsNodeAggregationWhereInput + OR: [typeBRelsNodeAggregationWhereInput!] + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") + } + + type typeBRelsRelationship { + cursor: String! + node: typeA! + properties: relType! + } + + input typeBRelsUpdateConnectionInput { + edge: relTypeUpdateInput + node: typeAUpdateInput + } + + input typeBRelsUpdateFieldInput { + connect: [typeBRelsConnectFieldInput!] + create: [typeBRelsCreateFieldInput!] + delete: [typeBRelsDeleteFieldInput!] + disconnect: [typeBRelsDisconnectFieldInput!] + update: typeBRelsUpdateConnectionInput + where: typeBRelsConnectionWhere + } + + \\"\\"\\" + Fields to sort TypeBS by. The order in which sorts are applied is not guaranteed when specifying many fields in one typeBSort object. + \\"\\"\\" + input typeBSort { + averageRating: SortDirection + cartesianPoint: SortDirection + createdAt: SortDirection + date: SortDirection + duration: SortDirection + id: SortDirection + localDateTime: SortDirection + localTime: SortDirection + point: SortDirection + time: SortDirection + } + + input typeBUpdateInput { + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + cartesianPoint: CartesianPointMutations + cartesianPoint_SET: CartesianPointInput @deprecated(reason: \\"Please use the generic mutation 'cartesianPoint: { set: ... } }' instead.\\") + createdAt: DateTimeScalarMutations + createdAt_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'createdAt: { set: ... } }' instead.\\") + date: DateScalarMutations + date_SET: Date @deprecated(reason: \\"Please use the generic mutation 'date: { set: ... } }' instead.\\") + duration: DurationScalarMutations + duration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'duration: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + list: ListStringMutations + list_POP: Int @deprecated(reason: \\"Please use the generic mutation 'list: { pop: ... } }' instead.\\") + list_PUSH: [String!] @deprecated(reason: \\"Please use the generic mutation 'list: { push: ... } }' instead.\\") + list_SET: [String!] @deprecated(reason: \\"Please use the generic mutation 'list: { set: ... } }' instead.\\") + localDateTime: LocalDateTimeScalarMutations + localDateTime_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'localDateTime: { set: ... } }' instead.\\") + localTime: LocalTimeScalarMutations + localTime_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'localTime: { set: ... } }' instead.\\") + point: PointMutations + point_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'point: { set: ... } }' instead.\\") + ratings: ListFloatMutations + ratings_POP: Int @deprecated(reason: \\"Please use the generic mutation 'ratings: { pop: ... } }' instead.\\") + ratings_PUSH: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { push: ... } }' instead.\\") + ratings_SET: [Float!] @deprecated(reason: \\"Please use the generic mutation 'ratings: { set: ... } }' instead.\\") + rels: [typeBRelsUpdateFieldInput!] + time: TimeScalarMutations + time_SET: Time @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") + } + + input typeBWhere { + AND: [typeBWhere!] + NOT: typeBWhere + OR: [typeBWhere!] + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float!] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + cartesianPoint: CartesianPointFilters + cartesianPoint_DISTANCE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { distance: ... }\\") + cartesianPoint_EQ: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { eq: ... }\\") + cartesianPoint_GT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gt: ... }\\") + cartesianPoint_GTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { gte: ... }\\") + cartesianPoint_IN: [CartesianPointInput] @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { in: ... }\\") + cartesianPoint_LT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lt: ... }\\") + cartesianPoint_LTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter cartesianPoint: { lte: ... }\\") + createdAt: DateTimeScalarFilters + createdAt_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { eq: ... }\\") + createdAt_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gt: ... }\\") + createdAt_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { gte: ... }\\") + createdAt_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter createdAt: { in: ... }\\") + createdAt_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lt: ... }\\") + createdAt_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter createdAt: { lte: ... }\\") + date: DateScalarFilters + date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter date: { eq: ... }\\") + date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gt: ... }\\") + date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gte: ... }\\") + date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter date: { in: ... }\\") + date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lt: ... }\\") + date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lte: ... }\\") + duration: DurationScalarFilters + duration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { eq: ... }\\") + duration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gt: ... }\\") + duration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gte: ... }\\") + duration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter duration: { in: ... }\\") + duration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lt: ... }\\") + duration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID!] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + list: StringListFilters + list_EQ: [String!] @deprecated(reason: \\"Please use the relevant generic filter list: { eq: ... }\\") + list_INCLUDES: String @deprecated(reason: \\"Please use the relevant generic filter list: { includes: ... }\\") + localDateTime: LocalDateTimeScalarFilters + localDateTime_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { eq: ... }\\") + localDateTime_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gt: ... }\\") + localDateTime_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { gte: ... }\\") + localDateTime_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { in: ... }\\") + localDateTime_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lt: ... }\\") + localDateTime_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDateTime: { lte: ... }\\") + localTime: LocalTimeScalarFilters + localTime_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { eq: ... }\\") + localTime_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gt: ... }\\") + localTime_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { gte: ... }\\") + localTime_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter localTime: { in: ... }\\") + localTime_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lt: ... }\\") + localTime_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter localTime: { lte: ... }\\") + point: PointFilters + point_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { distance: ... }\\") + point_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter point: { eq: ... }\\") + point_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gt: ... }\\") + point_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { gte: ... }\\") + point_IN: [PointInput] @deprecated(reason: \\"Please use the relevant generic filter point: { in: ... }\\") + point_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lt: ... }\\") + point_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter point: { lte: ... }\\") + ratings: FloatListFilters + ratings_EQ: [Float!] @deprecated(reason: \\"Please use the relevant generic filter ratings: { eq: ... }\\") + ratings_INCLUDES: Float @deprecated(reason: \\"Please use the relevant generic filter ratings: { includes: ... }\\") + rels: typeARelationshipFilters + relsAggregate: typeBRelsAggregateInput + relsConnection: typeBRelsConnectionFilters + time: TimeScalarFilters + time_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter time: { eq: ... }\\") + time_GT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gt: ... }\\") + time_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gte: ... }\\") + time_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter time: { in: ... }\\") + time_LT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lt: ... }\\") + time_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lte: ... }\\") + } + + type typeBtypeARelsAggregationSelection { + count: Int! + edge: typeBtypeARelsEdgeAggregateSelection + node: typeBtypeARelsNodeAggregateSelection + } + + type typeBtypeARelsEdgeAggregateSelection { + averageRating: FloatAggregateSelection! + createdAt: DateTimeAggregateSelection! + duration: DurationAggregateSelection! + localDateTime: LocalDateTimeAggregateSelection! + localTime: LocalTimeAggregateSelection! + time: TimeAggregateSelection! + } + + type typeBtypeARelsNodeAggregateSelection { + name: StringAggregateSelection! + }" + `); + }); +}); diff --git a/packages/graphql/tests/schema/remove-deprecated/typename_IN.test.ts b/packages/graphql/tests/schema/remove-deprecated/typename_IN.test.ts deleted file mode 100644 index 0b875f0fe3..0000000000 --- a/packages/graphql/tests/schema/remove-deprecated/typename_IN.test.ts +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { printSchemaWithDirectives } from "@graphql-tools/utils"; -import { lexicographicSortSchema } from "graphql/utilities"; -import { Neo4jGraphQL } from "../../../src"; - -describe("typename_IN", () => { - test("should remove typename_IN", async () => { - const typeDefs = /* GraphQL */ ` - type Series implements Production @node { - id: ID! - title: String! - numberOfEpisodes: Int! - } - - type Movie implements Production @node { - id: ID! - title: String! - } - - interface Production { - id: ID! - title: String! - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - excludeDeprecatedFields: { - typename_IN: true, - }, - }, - }); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements Production { - id: ID! - title: String! - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - input MovieCreateInput { - id: ID! - title: String! - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteMovies(where: MovieWhere): DeleteInfo! - deleteSeries(where: SeriesWhere): DeleteInfo! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - interface Production { - id: ID! - title: String! - } - - type ProductionAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - type ProductionEdge { - cursor: String! - node: Production! - } - - enum ProductionImplementation { - Movie - Series - } - - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] - } - - \\"\\"\\" - Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. - \\"\\"\\" - input ProductionSort { - id: SortDirection - title: SortDirection - } - - input ProductionWhere { - AND: [ProductionWhere!] - NOT: ProductionWhere - OR: [ProductionWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - typename: [ProductionImplementation!] - } - - type ProductionsConnection { - edges: [ProductionEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! - productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements Production { - id: ID! - numberOfEpisodes: Int! - title: String! - } - - type SeriesAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - numberOfEpisodes: IntAggregateSelection! - title: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - id: ID! - numberOfEpisodes: Int! - title: String! - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - id: SortDirection - numberOfEpisodes: SortDirection - title: SortDirection - } - - input SeriesUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - numberOfEpisodes: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - numberOfEpisodes_DECREMENT: Int - numberOfEpisodes_INCREMENT: Int - numberOfEpisodes_SET: Int - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID!] - id_STARTS_WITH: ID - numberOfEpisodes: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - numberOfEpisodes_EQ: Int - numberOfEpisodes_GT: Int - numberOfEpisodes_GTE: Int - numberOfEpisodes_IN: [Int!] - numberOfEpisodes_LT: Int - numberOfEpisodes_LTE: Int - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); -}); diff --git a/packages/graphql/tests/schema/rfcs/rfc-003.test.ts b/packages/graphql/tests/schema/rfcs/rfc-003.test.ts index 916b646a9e..88bdf1e563 100644 --- a/packages/graphql/tests/schema/rfcs/rfc-003.test.ts +++ b/packages/graphql/tests/schema/rfcs/rfc-003.test.ts @@ -17,8 +17,8 @@ * limitations under the License. */ -import { gql } from "graphql-tag"; import { GraphQLError, GraphQLSchema } from "graphql"; +import { gql } from "graphql-tag"; import { Neo4jGraphQL } from "../../../src"; describe("schema/rfc/003", () => { @@ -29,20 +29,18 @@ describe("schema/rfc/003", () => { const typeDefs = gql` type Source @node { targets: [Target!]! @relationship(type: "HAS_TARGET", direction: OUT) - target1: SecondTarget! @relationship(type: "HAS_TARGET", direction: OUT) - target2: ThirdTarget @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } type SecondTarget @node { - id: ID @id @unique + id: ID @id } type ThirdTarget @node { - id: ID @id @unique + id: ID @id } `; @@ -58,7 +56,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -74,7 +72,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -89,7 +87,7 @@ describe("schema/rfc/003", () => { } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -104,19 +102,15 @@ describe("schema/rfc/003", () => { const typeDefs = gql` interface SourceInterface { targets: [Target!]! @declareRelationship - target1: Target! @declareRelationship - target2: Target @declareRelationship } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target!]! @relationship(type: "HAS_TARGET", direction: OUT) - target1: Target! @relationship(type: "HAS_TARGET", direction: OUT) - target2: Target @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -132,12 +126,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target!] @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -156,12 +150,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target]! @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; @@ -180,12 +174,12 @@ describe("schema/rfc/003", () => { } type Source implements SourceInterface @node { - id: ID @id @unique + id: ID @id targets: [Target] @relationship(type: "HAS_TARGET", direction: OUT) } type Target @node { - id: ID @id @unique + id: ID @id } `; diff --git a/packages/graphql/tests/schema/scalar.test.ts b/packages/graphql/tests/schema/scalar.test.ts index fde3ef35ed..d9d1441921 100644 --- a/packages/graphql/tests/schema/scalar.test.ts +++ b/packages/graphql/tests/schema/scalar.test.ts @@ -58,6 +58,30 @@ describe("Scalar", () => { scalar CustomScalar + \\"\\"\\"CustomScalar filters\\"\\"\\" + input CustomScalarListScalarFilters { + eq: [CustomScalar!] + includes: CustomScalar + } + + \\"\\"\\"Mutations for a list for CustomScalar\\"\\"\\" + input CustomScalarListScalarMutations { + pop: CustomScalar + push: [CustomScalar!]! + set: [CustomScalar!]! + } + + \\"\\"\\"CustomScalar filters\\"\\"\\" + input CustomScalarScalarFilters { + eq: CustomScalar + in: [CustomScalar!] + } + + \\"\\"\\"CustomScalar filters\\"\\"\\" + input CustomScalarScalarMutations { + set: CustomScalar + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -66,9 +90,18 @@ describe("Scalar", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -80,7 +113,6 @@ describe("Scalar", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -95,15 +127,6 @@ describe("Scalar", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -113,35 +136,35 @@ describe("Scalar", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - myCustomArrayScalar: [CustomScalar!] @deprecated(reason: \\"Please use the explicit _SET field\\") - myCustomArrayScalar_SET: [CustomScalar!] - myCustomScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _SET field\\") - myCustomScalar_SET: CustomScalar - myRequiredCustomArrayScalar: [CustomScalar!] @deprecated(reason: \\"Please use the explicit _SET field\\") - myRequiredCustomArrayScalar_SET: [CustomScalar!] + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + myCustomArrayScalar: CustomScalarListScalarMutations + myCustomArrayScalar_SET: [CustomScalar!] @deprecated(reason: \\"Please use the generic mutation 'myCustomArrayScalar: { set: ... } }' instead.\\") + myCustomScalar: CustomScalarScalarMutations + myCustomScalar_SET: CustomScalar @deprecated(reason: \\"Please use the generic mutation 'myCustomScalar: { set: ... } }' instead.\\") + myRequiredCustomArrayScalar: CustomScalarListScalarMutations + myRequiredCustomArrayScalar_SET: [CustomScalar!] @deprecated(reason: \\"Please use the generic mutation 'myRequiredCustomArrayScalar: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - myCustomArrayScalar: [CustomScalar!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - myCustomArrayScalar_EQ: [CustomScalar!] - myCustomArrayScalar_INCLUDES: CustomScalar - myCustomScalar: CustomScalar @deprecated(reason: \\"Please use the explicit _EQ version\\") - myCustomScalar_EQ: CustomScalar - myCustomScalar_IN: [CustomScalar] - myRequiredCustomArrayScalar: [CustomScalar!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - myRequiredCustomArrayScalar_EQ: [CustomScalar!] - myRequiredCustomArrayScalar_INCLUDES: CustomScalar + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + myCustomArrayScalar: CustomScalarListScalarFilters + myCustomArrayScalar_EQ: [CustomScalar!] @deprecated(reason: \\"Please use the relevant generic filter myCustomArrayScalar: { eq: ... }\\") + myCustomArrayScalar_INCLUDES: CustomScalar @deprecated(reason: \\"Please use the relevant generic filter myCustomArrayScalar: { includes: ... }\\") + myCustomScalar: CustomScalarScalarFilters + myCustomScalar_EQ: CustomScalar @deprecated(reason: \\"Please use the relevant generic filter myCustomScalar: { eq: ... }\\") + myCustomScalar_IN: [CustomScalar] @deprecated(reason: \\"Please use the relevant generic filter myCustomScalar: { in: ... }\\") + myRequiredCustomArrayScalar: CustomScalarListScalarFilters + myRequiredCustomArrayScalar_EQ: [CustomScalar!] @deprecated(reason: \\"Please use the relevant generic filter myRequiredCustomArrayScalar: { eq: ... }\\") + myRequiredCustomArrayScalar_INCLUDES: CustomScalar @deprecated(reason: \\"Please use the relevant generic filter myRequiredCustomArrayScalar: { includes: ... }\\") } type MoviesConnection { @@ -165,7 +188,7 @@ describe("Scalar", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/simple.test.ts b/packages/graphql/tests/schema/simple.test.ts index 08a7cc95f0..972c865ae4 100644 --- a/packages/graphql/tests/schema/simple.test.ts +++ b/packages/graphql/tests/schema/simple.test.ts @@ -41,6 +41,16 @@ describe("Simple", () => { mutation: Mutation } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -69,9 +79,37 @@ describe("Simple", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -81,6 +119,23 @@ describe("Simple", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int averageRating: Float @@ -92,7 +147,6 @@ describe("Simple", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -107,15 +161,6 @@ describe("Simple", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -127,48 +172,48 @@ describe("Simple", () => { } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -192,7 +237,7 @@ describe("Simple", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/string-comparators.test.ts b/packages/graphql/tests/schema/string-comparators.test.ts index 20689225f5..092d57c0ff 100644 --- a/packages/graphql/tests/schema/string-comparators.test.ts +++ b/packages/graphql/tests/schema/string-comparators.test.ts @@ -89,15 +89,6 @@ describe("String Comparators", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -106,24 +97,24 @@ describe("String Comparators", () => { } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_GT: String - title_GTE: String - title_IN: [String] - title_LT: String - title_LTE: String - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_GT: String @deprecated(reason: \\"Please use the relevant generic filter title: { gt: ... }\\") + title_GTE: String @deprecated(reason: \\"Please use the relevant generic filter title: { gte: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_LT: String @deprecated(reason: \\"Please use the relevant generic filter title: { lt: ... }\\") + title_LTE: String @deprecated(reason: \\"Please use the relevant generic filter title: { lte: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -147,7 +138,7 @@ describe("String Comparators", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -165,6 +156,24 @@ describe("String Comparators", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + gt: String + gte: String + in: [String!] + lt: String + lte: String + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -238,15 +247,6 @@ describe("String Comparators", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -255,20 +255,20 @@ describe("String Comparators", () => { } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -292,7 +292,7 @@ describe("String Comparators", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -310,6 +310,20 @@ describe("String Comparators", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -392,15 +406,6 @@ describe("String Comparators", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -409,22 +414,22 @@ describe("String Comparators", () => { } input MovieUpdateInput { - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_GT: String - title_IN: [String] - title_LT: String - title_STARTS_WITH: String + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_GT: String @deprecated(reason: \\"Please use the relevant generic filter title: { gt: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_LT: String @deprecated(reason: \\"Please use the relevant generic filter title: { lt: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -448,7 +453,7 @@ describe("String Comparators", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -466,6 +471,22 @@ describe("String Comparators", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + gt: String + in: [String!] + lt: String + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" @@ -534,21 +555,22 @@ describe("String Comparators", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_LENGTH_EQUAL: Float - screenTime_AVERAGE_LENGTH_GT: Float - screenTime_AVERAGE_LENGTH_GTE: Float - screenTime_AVERAGE_LENGTH_LT: Float - screenTime_AVERAGE_LENGTH_LTE: Float - screenTime_LONGEST_LENGTH_EQUAL: Int - screenTime_LONGEST_LENGTH_GT: Int - screenTime_LONGEST_LENGTH_GTE: Int - screenTime_LONGEST_LENGTH_LT: Int - screenTime_LONGEST_LENGTH_LTE: Int - screenTime_SHORTEST_LENGTH_EQUAL: Int - screenTime_SHORTEST_LENGTH_GT: Int - screenTime_SHORTEST_LENGTH_GTE: Int - screenTime_SHORTEST_LENGTH_LT: Int - screenTime_SHORTEST_LENGTH_LTE: Int + screenTime: StringScalarAggregationFilters + screenTime_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { averageLength: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { averageLength: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { averageLength: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { averageLength: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { averageLength: { lte: ... } } }' instead.\\") + screenTime_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { longestLength: { eq: ... } } }' instead.\\") + screenTime_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { longestLength: { gt: ... } } }' instead.\\") + screenTime_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { longestLength: { gte: ... } } }' instead.\\") + screenTime_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { longestLength: { lt: ... } } }' instead.\\") + screenTime_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { longestLength: { lte: ... } } }' instead.\\") + screenTime_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { shortestLength: { eq: ... } } }' instead.\\") + screenTime_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { shortestLength: { gt: ... } } }' instead.\\") + screenTime_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { shortestLength: { gte: ... } } }' instead.\\") + screenTime_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { shortestLength: { lt: ... } } }' instead.\\") + screenTime_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { shortestLength: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -560,30 +582,30 @@ describe("String Comparators", () => { } input ActedInUpdateInput { - screenTime: String @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_SET: String + screenTime: StringScalarMutations + screenTime_SET: String @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_CONTAINS: String - screenTime_ENDS_WITH: String - screenTime_EQ: String - screenTime_GT: String - screenTime_GTE: String - screenTime_IN: [String] - screenTime_LT: String - screenTime_LTE: String - screenTime_STARTS_WITH: String + screenTime: StringScalarFilters + screenTime_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { contains: ... }\\") + screenTime_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { endsWith: ... }\\") + screenTime_EQ: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") + screenTime_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter screenTime: { startsWith: ... }\\") } type Actor { - actedIn(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - actedInAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieActedInAggregationSelection - actedInConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! + actedIn(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + actedInAggregate(where: MovieWhere): ActorMovieActedInAggregationSelection + actedInConnection(after: String, first: Int, sort: [ActorActedInConnectionSort!], where: ActorActedInConnectionWhere): ActorActedInConnection! name: String } @@ -591,7 +613,7 @@ describe("String Comparators", () => { AND: [ActorActedInAggregateInput!] NOT: ActorActedInAggregateInput OR: [ActorActedInAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -604,10 +626,6 @@ describe("String Comparators", () => { input ActorActedInConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -617,6 +635,25 @@ describe("String Comparators", () => { totalCount: Int! } + input ActorActedInConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorActedInConnections match this filter + \\"\\"\\" + all: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorActedInConnections match this filter + \\"\\"\\" + none: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorActedInConnections match this filter + \\"\\"\\" + single: ActorActedInConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorActedInConnections match this filter + \\"\\"\\" + some: ActorActedInConnectionWhere + } + input ActorActedInConnectionSort { edge: ActedInSort node: MovieSort @@ -654,21 +691,22 @@ describe("String Comparators", () => { AND: [ActorActedInNodeAggregationWhereInput!] NOT: ActorActedInNodeAggregationWhereInput OR: [ActorActedInNodeAggregationWhereInput!] - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorActedInRelationship { @@ -736,13 +774,15 @@ describe("String Comparators", () => { title: StringAggregateSelection! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -754,49 +794,51 @@ describe("String Comparators", () => { input ActorUpdateInput { actedIn: [ActorActedInUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + actedIn: MovieRelationshipFilters actedInAggregate: ActorActedInAggregateInput + actedInConnection: ActorActedInConnectionFilters \\"\\"\\" Return Actors where all of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_ALL: ActorActedInConnectionWhere + actedInConnection_ALL: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_NONE: ActorActedInConnectionWhere + actedInConnection_NONE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SINGLE: ActorActedInConnectionWhere + actedInConnection_SINGLE: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorActedInConnections match this filter \\"\\"\\" - actedInConnection_SOME: ActorActedInConnectionWhere + actedInConnection_SOME: ActorActedInConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedInConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - actedIn_ALL: MovieWhere + actedIn_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - actedIn_NONE: MovieWhere + actedIn_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - actedIn_SINGLE: MovieWhere + actedIn_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - actedIn_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_GT: String - name_GTE: String - name_IN: [String] - name_LT: String - name_LTE: String - name_STARTS_WITH: String + actedIn_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'actedIn: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_GT: String @deprecated(reason: \\"Please use the relevant generic filter name: { gt: ... }\\") + name_GTE: String @deprecated(reason: \\"Please use the relevant generic filter name: { gte: ... }\\") + name_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_LT: String @deprecated(reason: \\"Please use the relevant generic filter name: { lt: ... }\\") + name_LTE: String @deprecated(reason: \\"Please use the relevant generic filter name: { lte: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -831,10 +873,30 @@ describe("String Comparators", () => { relationshipsDeleted: Int! } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! title: String } @@ -856,7 +918,7 @@ describe("String Comparators", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -869,10 +931,6 @@ describe("String Comparators", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -882,6 +940,25 @@ describe("String Comparators", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -919,21 +996,22 @@ describe("String Comparators", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -987,13 +1065,15 @@ describe("String Comparators", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1005,49 +1085,51 @@ describe("String Comparators", () => { input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_GT: String - title_GTE: String - title_IN: [String] - title_LT: String - title_LTE: String - title_STARTS_WITH: String + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_GT: String @deprecated(reason: \\"Please use the relevant generic filter title: { gt: ... }\\") + title_GTE: String @deprecated(reason: \\"Please use the relevant generic filter title: { gte: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_LT: String @deprecated(reason: \\"Please use the relevant generic filter title: { lt: ... }\\") + title_LTE: String @deprecated(reason: \\"Please use the relevant generic filter title: { lte: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1074,10 +1156,10 @@ describe("String Comparators", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1095,6 +1177,31 @@ describe("String Comparators", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + gt: String + gte: String + in: [String!] + lt: String + lte: String + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/subscriptions.test.ts b/packages/graphql/tests/schema/subscriptions.test.ts index 467a12c2ad..1bf2961046 100644 --- a/packages/graphql/tests/schema/subscriptions.test.ts +++ b/packages/graphql/tests/schema/subscriptions.test.ts @@ -91,13 +91,15 @@ describe("Subscriptions", () => { name: String! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -111,17 +113,17 @@ describe("Subscriptions", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -135,12 +137,12 @@ describe("Subscriptions", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -149,6 +151,16 @@ describe("Subscriptions", () => { totalCount: Int! } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -190,9 +202,37 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -202,11 +242,28 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -225,7 +282,7 @@ describe("Subscriptions", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -235,10 +292,6 @@ describe("Subscriptions", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -248,6 +301,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -280,21 +352,22 @@ describe("Subscriptions", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -319,7 +392,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -358,15 +430,6 @@ describe("Subscriptions", () => { isActive: Boolean } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -381,46 +444,46 @@ describe("Subscriptions", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -434,53 +497,55 @@ describe("Subscriptions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -507,10 +572,10 @@ describe("Subscriptions", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -528,6 +593,27 @@ describe("Subscriptions", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -591,9 +677,9 @@ describe("Subscriptions", () => { } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! } type ActorAggregateSelection { @@ -643,14 +729,13 @@ describe("Subscriptions", () => { type ActorMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ActorMoviesAggregateInput { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -661,10 +746,6 @@ describe("Subscriptions", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -674,6 +755,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -708,56 +808,48 @@ describe("Subscriptions", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -778,9 +870,15 @@ describe("Subscriptions", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } input ActorUpdateInput { @@ -796,31 +894,33 @@ describe("Subscriptions", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type ActorsConnection { @@ -829,6 +929,16 @@ describe("Subscriptions", () => { totalCount: Int! } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -870,9 +980,45 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -882,11 +1028,36 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -900,7 +1071,7 @@ describe("Subscriptions", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -910,10 +1081,6 @@ describe("Subscriptions", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -923,6 +1090,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { AND: [MovieActorsConnectionWhere!] NOT: MovieActorsConnectionWhere @@ -971,7 +1157,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -1022,13 +1207,15 @@ describe("Subscriptions", () => { isActive: Boolean } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1045,46 +1232,46 @@ describe("Subscriptions", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -1098,53 +1285,55 @@ describe("Subscriptions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -1171,10 +1360,10 @@ describe("Subscriptions", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -1256,11 +1445,32 @@ describe("Subscriptions", () => { union Actor = Person | Star + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + input ActorWhere { Person: PersonWhere Star: StarWhere } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -1307,9 +1517,45 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -1319,10 +1565,35 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -1339,6 +1610,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Person: MovieActorsPersonConnectionWhere Star: MovieActorsStarConnectionWhere @@ -1461,7 +1751,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -1512,13 +1801,15 @@ describe("Subscriptions", () => { isActive: Boolean } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -1535,46 +1826,46 @@ describe("Subscriptions", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: MovieActorsUpdateInput - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -1588,52 +1879,54 @@ describe("Subscriptions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -1669,9 +1962,9 @@ describe("Subscriptions", () => { } type Person { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): PersonMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): PersonMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! } type PersonAggregateSelection { @@ -1721,14 +2014,13 @@ describe("Subscriptions", () => { type PersonMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PersonMoviesAggregateInput { AND: [PersonMoviesAggregateInput!] NOT: PersonMoviesAggregateInput OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1739,10 +2031,6 @@ describe("Subscriptions", () => { input PersonMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -1752,6 +2040,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + all: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + none: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + single: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + some: PersonMoviesConnectionWhere + } + input PersonMoviesConnectionSort { node: MovieSort } @@ -1786,56 +2093,48 @@ describe("Subscriptions", () => { AND: [PersonMoviesNodeAggregationWhereInput!] NOT: PersonMoviesNodeAggregationWhereInput OR: [PersonMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type PersonMoviesRelationship { @@ -1856,11 +2155,6 @@ describe("Subscriptions", () => { where: PersonMoviesConnectionWhere } - input PersonOptions { - limit: Int - offset: Int - } - input PersonUpdateInput { movies: [PersonMoviesUpdateFieldInput!] } @@ -1874,52 +2168,48 @@ describe("Subscriptions", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] + movies: MovieRelationshipFilters moviesAggregate: PersonMoviesAggregateInput + moviesConnection: PersonMoviesConnectionFilters \\"\\"\\" Return People where all of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: PersonMoviesConnectionWhere + moviesConnection_ALL: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: PersonMoviesConnectionWhere + moviesConnection_NONE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: PersonMoviesConnectionWhere + moviesConnection_SINGLE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: PersonMoviesConnectionWhere + moviesConnection_SOME: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type Query { - actors(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, where: PersonWhere): PeopleConnection! - stars(limit: Int, offset: Int, options: StarOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: StarWhere): [Star!]! + stars(limit: Int, offset: Int, where: StarWhere): [Star!]! starsAggregate(where: StarWhere): StarAggregateSelection! starsConnection(after: String, first: Int, where: StarWhere): StarsConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -1929,9 +2219,9 @@ describe("Subscriptions", () => { } type Star { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): StarMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [StarMoviesConnectionSort!], where: StarMoviesConnectionWhere): StarMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): StarMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [StarMoviesConnectionSort!], where: StarMoviesConnectionWhere): StarMoviesConnection! } type StarAggregateSelection { @@ -1981,14 +2271,13 @@ describe("Subscriptions", () => { type StarMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input StarMoviesAggregateInput { AND: [StarMoviesAggregateInput!] NOT: StarMoviesAggregateInput OR: [StarMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1999,10 +2288,6 @@ describe("Subscriptions", () => { input StarMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -2012,6 +2297,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input StarMoviesConnectionFilters { + \\"\\"\\" + Return Stars where all of the related StarMoviesConnections match this filter + \\"\\"\\" + all: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where none of the related StarMoviesConnections match this filter + \\"\\"\\" + none: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where one of the related StarMoviesConnections match this filter + \\"\\"\\" + single: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where some of the related StarMoviesConnections match this filter + \\"\\"\\" + some: StarMoviesConnectionWhere + } + input StarMoviesConnectionSort { node: MovieSort } @@ -2046,56 +2350,48 @@ describe("Subscriptions", () => { AND: [StarMoviesNodeAggregationWhereInput!] NOT: StarMoviesNodeAggregationWhereInput OR: [StarMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type StarMoviesRelationship { @@ -2116,11 +2412,6 @@ describe("Subscriptions", () => { where: StarMoviesConnectionWhere } - input StarOptions { - limit: Int - offset: Int - } - input StarUpdateInput { movies: [StarMoviesUpdateFieldInput!] } @@ -2134,31 +2425,33 @@ describe("Subscriptions", () => { AND: [StarWhere!] NOT: StarWhere OR: [StarWhere!] + movies: MovieRelationshipFilters moviesAggregate: StarMoviesAggregateInput + moviesConnection: StarMoviesConnectionFilters \\"\\"\\" Return Stars where all of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: StarMoviesConnectionWhere + moviesConnection_ALL: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where none of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: StarMoviesConnectionWhere + moviesConnection_NONE: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where one of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: StarMoviesConnectionWhere + moviesConnection_SINGLE: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where some of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: StarMoviesConnectionWhere + moviesConnection_SOME: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Stars where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Stars where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Stars where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Stars where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type StarsConnection { @@ -2253,26 +2546,27 @@ describe("Subscriptions", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -2284,29 +2578,29 @@ describe("Subscriptions", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! } type ActorAggregateSelection { @@ -2356,14 +2650,13 @@ describe("Subscriptions", () => { type ActorMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input ActorMoviesAggregateInput { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2374,10 +2667,6 @@ describe("Subscriptions", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -2387,6 +2676,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { node: MovieSort } @@ -2421,56 +2729,48 @@ describe("Subscriptions", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -2491,9 +2791,15 @@ describe("Subscriptions", () => { where: ActorMoviesConnectionWhere } - input ActorOptions { - limit: Int - offset: Int + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } input ActorUpdateInput { @@ -2509,31 +2815,33 @@ describe("Subscriptions", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type ActorsConnection { @@ -2542,6 +2850,16 @@ describe("Subscriptions", () => { totalCount: Int! } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -2583,9 +2901,45 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -2595,11 +2949,36 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -2618,7 +2997,7 @@ describe("Subscriptions", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -2630,10 +3009,6 @@ describe("Subscriptions", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -2643,6 +3018,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort } @@ -2699,7 +3093,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -2750,13 +3143,15 @@ describe("Subscriptions", () => { isActive: Boolean } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -2773,46 +3168,46 @@ describe("Subscriptions", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -2826,53 +3221,55 @@ describe("Subscriptions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -2899,10 +3296,10 @@ describe("Subscriptions", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -3013,13 +3410,15 @@ describe("Subscriptions", () => { name: String! } - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -3033,17 +3432,17 @@ describe("Subscriptions", () => { AND: [ActorSubscriptionWhere!] NOT: ActorSubscriptionWhere OR: [ActorSubscriptionWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } input ActorUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } type ActorUpdatedEvent { @@ -3057,12 +3456,12 @@ describe("Subscriptions", () => { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -3071,6 +3470,16 @@ describe("Subscriptions", () => { totalCount: Int! } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + type CreateActorsMutationResponse { actors: [Actor!]! info: CreateInfo! @@ -3112,9 +3521,37 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -3124,11 +3561,28 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -3147,7 +3601,7 @@ describe("Subscriptions", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -3157,10 +3611,6 @@ describe("Subscriptions", () => { } input MovieActorsConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } @@ -3170,6 +3620,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { node: ActorSort } @@ -3202,21 +3671,22 @@ describe("Subscriptions", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -3241,7 +3711,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -3261,15 +3730,6 @@ describe("Subscriptions", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -3281,74 +3741,76 @@ describe("Subscriptions", () => { } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: [MovieActorsUpdateFieldInput!] - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -3375,10 +3837,10 @@ describe("Subscriptions", () => { } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -3396,6 +3858,27 @@ describe("Subscriptions", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type Subscription { actorCreated(where: ActorSubscriptionWhere): ActorCreatedEvent! actorDeleted(where: ActorSubscriptionWhere): ActorDeletedEvent! @@ -3424,447 +3907,6 @@ describe("Subscriptions", () => { `); }); - test("Type with relationship to a subscriptions excluded type", async () => { - const typeDefs = gql` - type User @mutation(operations: []) @subscription(events: []) @node { - username: String! - name: String - } - type Agreement @node { - id: Int! - name: String - owner: User @relationship(type: "OWNED_BY", direction: OUT) - } - `; - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - subscriptions: new TestCDCEngine(), - }, - }); - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - type Agreement { - id: Int! - name: String - owner(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): User - ownerAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: UserWhere): AgreementUserOwnerAggregationSelection - ownerConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [AgreementOwnerConnectionSort!], where: AgreementOwnerConnectionWhere): AgreementOwnerConnection! - } - - type AgreementAggregateSelection { - count: Int! - id: IntAggregateSelection! - name: StringAggregateSelection! - } - - input AgreementCreateInput { - id: Int! - name: String - owner: AgreementOwnerFieldInput - } - - type AgreementCreatedEvent { - createdAgreement: AgreementEventPayload! - event: EventType! - timestamp: Float! - } - - input AgreementDeleteInput { - owner: AgreementOwnerDeleteFieldInput - } - - type AgreementDeletedEvent { - deletedAgreement: AgreementEventPayload! - event: EventType! - timestamp: Float! - } - - type AgreementEdge { - cursor: String! - node: Agreement! - } - - type AgreementEventPayload { - id: Int! - name: String - } - - input AgreementOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more AgreementSort objects to sort Agreements by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [AgreementSort!] - } - - input AgreementOwnerAggregateInput { - AND: [AgreementOwnerAggregateInput!] - NOT: AgreementOwnerAggregateInput - OR: [AgreementOwnerAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: AgreementOwnerNodeAggregationWhereInput - } - - input AgreementOwnerConnectFieldInput { - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") - where: UserConnectWhere - } - - type AgreementOwnerConnection { - edges: [AgreementOwnerRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input AgreementOwnerConnectionSort { - node: UserSort - } - - input AgreementOwnerConnectionWhere { - AND: [AgreementOwnerConnectionWhere!] - NOT: AgreementOwnerConnectionWhere - OR: [AgreementOwnerConnectionWhere!] - node: UserWhere - } - - input AgreementOwnerCreateFieldInput { - node: UserCreateInput! - } - - input AgreementOwnerDeleteFieldInput { - where: AgreementOwnerConnectionWhere - } - - input AgreementOwnerDisconnectFieldInput { - where: AgreementOwnerConnectionWhere - } - - input AgreementOwnerFieldInput { - connect: AgreementOwnerConnectFieldInput - create: AgreementOwnerCreateFieldInput - } - - input AgreementOwnerNodeAggregationWhereInput { - AND: [AgreementOwnerNodeAggregationWhereInput!] - NOT: AgreementOwnerNodeAggregationWhereInput - OR: [AgreementOwnerNodeAggregationWhereInput!] - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int - username_AVERAGE_LENGTH_EQUAL: Float - username_AVERAGE_LENGTH_GT: Float - username_AVERAGE_LENGTH_GTE: Float - username_AVERAGE_LENGTH_LT: Float - username_AVERAGE_LENGTH_LTE: Float - username_LONGEST_LENGTH_EQUAL: Int - username_LONGEST_LENGTH_GT: Int - username_LONGEST_LENGTH_GTE: Int - username_LONGEST_LENGTH_LT: Int - username_LONGEST_LENGTH_LTE: Int - username_SHORTEST_LENGTH_EQUAL: Int - username_SHORTEST_LENGTH_GT: Int - username_SHORTEST_LENGTH_GTE: Int - username_SHORTEST_LENGTH_LT: Int - username_SHORTEST_LENGTH_LTE: Int - } - - type AgreementOwnerRelationship { - cursor: String! - node: User! - } - - input AgreementOwnerUpdateConnectionInput { - node: UserUpdateInput - } - - input AgreementOwnerUpdateFieldInput { - connect: AgreementOwnerConnectFieldInput - create: AgreementOwnerCreateFieldInput - delete: AgreementOwnerDeleteFieldInput - disconnect: AgreementOwnerDisconnectFieldInput - update: AgreementOwnerUpdateConnectionInput - where: AgreementOwnerConnectionWhere - } - - \\"\\"\\" - Fields to sort Agreements by. The order in which sorts are applied is not guaranteed when specifying many fields in one AgreementSort object. - \\"\\"\\" - input AgreementSort { - id: SortDirection - name: SortDirection - } - - input AgreementSubscriptionWhere { - AND: [AgreementSubscriptionWhere!] - NOT: AgreementSubscriptionWhere - OR: [AgreementSubscriptionWhere!] - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - id_GT: Int - id_GTE: Int - id_IN: [Int!] - id_LT: Int - id_LTE: Int - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - } - - input AgreementUpdateInput { - id: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - id_DECREMENT: Int - id_INCREMENT: Int - id_SET: Int - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - owner: AgreementOwnerUpdateFieldInput - } - - type AgreementUpdatedEvent { - event: EventType! - previousState: AgreementEventPayload! - timestamp: Float! - updatedAgreement: AgreementEventPayload! - } - - type AgreementUserOwnerAggregationSelection { - count: Int! - node: AgreementUserOwnerNodeAggregateSelection - } - - type AgreementUserOwnerNodeAggregateSelection { - name: StringAggregateSelection! - username: StringAggregateSelection! - } - - input AgreementWhere { - AND: [AgreementWhere!] - NOT: AgreementWhere - OR: [AgreementWhere!] - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - id_GT: Int - id_GTE: Int - id_IN: [Int!] - id_LT: Int - id_LTE: Int - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - owner: UserWhere - ownerAggregate: AgreementOwnerAggregateInput - ownerConnection: AgreementOwnerConnectionWhere - } - - type AgreementsConnection { - edges: [AgreementEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type CreateAgreementsMutationResponse { - agreements: [Agreement!]! - info: CreateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Mutation { - createAgreements(input: [AgreementCreateInput!]!): CreateAgreementsMutationResponse! - deleteAgreements(delete: AgreementDeleteInput, where: AgreementWhere): DeleteInfo! - updateAgreements(update: AgreementUpdateInput, where: AgreementWhere): UpdateAgreementsMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type Query { - agreements(limit: Int, offset: Int, options: AgreementOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [AgreementSort!], where: AgreementWhere): [Agreement!]! - agreementsAggregate(where: AgreementWhere): AgreementAggregateSelection! - agreementsConnection(after: String, first: Int, sort: [AgreementSort!], where: AgreementWhere): AgreementsConnection! - users(limit: Int, offset: Int, options: UserOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [UserSort!], where: UserWhere): [User!]! - usersAggregate(where: UserWhere): UserAggregateSelection! - usersConnection(after: String, first: Int, sort: [UserSort!], where: UserWhere): UsersConnection! - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - agreementCreated(where: AgreementSubscriptionWhere): AgreementCreatedEvent! - agreementDeleted(where: AgreementSubscriptionWhere): AgreementDeletedEvent! - agreementUpdated(where: AgreementSubscriptionWhere): AgreementUpdatedEvent! - } - - type UpdateAgreementsMutationResponse { - agreements: [Agreement!]! - info: UpdateInfo! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type User { - name: String - username: String! - } - - type UserAggregateSelection { - count: Int! - name: StringAggregateSelection! - username: StringAggregateSelection! - } - - input UserConnectWhere { - node: UserWhere! - } - - input UserCreateInput { - name: String - username: String! - } - - type UserEdge { - cursor: String! - node: User! - } - - input UserOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more UserSort objects to sort Users by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [UserSort!] - } - - \\"\\"\\" - Fields to sort Users by. The order in which sorts are applied is not guaranteed when specifying many fields in one UserSort object. - \\"\\"\\" - input UserSort { - name: SortDirection - username: SortDirection - } - - input UserUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - username: String @deprecated(reason: \\"Please use the explicit _SET field\\") - username_SET: String - } - - input UserWhere { - AND: [UserWhere!] - NOT: UserWhere - OR: [UserWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String] - name_STARTS_WITH: String - username: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - username_CONTAINS: String - username_ENDS_WITH: String - username_EQ: String - username_IN: [String!] - username_STARTS_WITH: String - } - - type UsersConnection { - edges: [UserEdge!]! - pageInfo: PageInfo! - totalCount: Int! - }" - `); - }); - test("Type with relationship to a subscriptions excluded type + Union type", async () => { const typeDefs = gql` type Movie @node { @@ -3903,11 +3945,32 @@ describe("Subscriptions", () => { union Actor = Person | Star + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere + } + input ActorWhere { Person: PersonWhere Star: StarWhere } + \\"\\"\\"Boolean filters\\"\\"\\" + input BooleanScalarFilters { + eq: Boolean + } + + \\"\\"\\"Boolean mutations\\"\\"\\" + input BooleanScalarMutations { + set: Boolean + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -3954,9 +4017,45 @@ describe("Subscriptions", () => { sum: Float } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Filters for an aggregation of a float field\\"\\"\\" + input FloatScalarAggregationFilters { + average: FloatScalarFilters + max: FloatScalarFilters + min: FloatScalarFilters + sum: FloatScalarFilters + } + + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + + \\"\\"\\"Float mutations\\"\\"\\" + input FloatScalarMutations { + add: Float + divide: Float + multiply: Float + set: Float + subtract: Float + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type IntAggregateSelection { @@ -3966,10 +4065,35 @@ describe("Subscriptions", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { actorCount: Int - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + actorsConnection(after: String, first: Int, where: MovieActorsConnectionWhere): MovieActorsConnection! averageRating: Float id: ID isActive: Boolean @@ -3986,6 +4110,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionWhere { Person: MovieActorsPersonConnectionWhere Star: MovieActorsStarConnectionWhere @@ -4108,7 +4251,6 @@ describe("Subscriptions", () => { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -4159,13 +4301,15 @@ describe("Subscriptions", () => { isActive: Boolean } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } \\"\\"\\" @@ -4182,46 +4326,46 @@ describe("Subscriptions", () => { AND: [MovieSubscriptionWhere!] NOT: MovieSubscriptionWhere OR: [MovieSubscriptionWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } input MovieUpdateInput { - actorCount: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - actorCount_DECREMENT: Int - actorCount_INCREMENT: Int - actorCount_SET: Int + actorCount: IntScalarMutations + actorCount_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { decrement: ... } }' instead.\\") + actorCount_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'actorCount: { increment: ... } }' instead.\\") + actorCount_SET: Int @deprecated(reason: \\"Please use the generic mutation 'actorCount: { set: ... } }' instead.\\") actors: MovieActorsUpdateInput - averageRating: Float @deprecated(reason: \\"Please use the explicit _SET field\\") - averageRating_ADD: Float - averageRating_DIVIDE: Float - averageRating_MULTIPLY: Float - averageRating_SET: Float - averageRating_SUBTRACT: Float - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _SET field\\") - isActive_SET: Boolean + averageRating: FloatScalarMutations + averageRating_ADD: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { add: ... } }' instead.\\") + averageRating_DIVIDE: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { divide: ... } }' instead.\\") + averageRating_MULTIPLY: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { multiply: ... } }' instead.\\") + averageRating_SET: Float @deprecated(reason: \\"Please use the generic mutation 'averageRating: { set: ... } }' instead.\\") + averageRating_SUBTRACT: Float @deprecated(reason: \\"Please use the relevant generic mutation 'averageRating: { subtract: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + isActive: BooleanScalarMutations + isActive_SET: Boolean @deprecated(reason: \\"Please use the generic mutation 'isActive: { set: ... } }' instead.\\") } type MovieUpdatedEvent { @@ -4235,52 +4379,54 @@ describe("Subscriptions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - actorCount: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - actorCount_EQ: Int - actorCount_GT: Int - actorCount_GTE: Int - actorCount_IN: [Int] - actorCount_LT: Int - actorCount_LTE: Int + actorCount: IntScalarFilters + actorCount_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { eq: ... }\\") + actorCount_GT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gt: ... }\\") + actorCount_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { gte: ... }\\") + actorCount_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter actorCount: { in: ... }\\") + actorCount_LT: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lt: ... }\\") + actorCount_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter actorCount: { lte: ... }\\") + actors: ActorRelationshipFilters + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere - averageRating: Float @deprecated(reason: \\"Please use the explicit _EQ version\\") - averageRating_EQ: Float - averageRating_GT: Float - averageRating_GTE: Float - averageRating_IN: [Float] - averageRating_LT: Float - averageRating_LTE: Float - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - isActive: Boolean @deprecated(reason: \\"Please use the explicit _EQ version\\") - isActive_EQ: Boolean + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + averageRating: FloatScalarFilters + averageRating_EQ: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { eq: ... }\\") + averageRating_GT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gt: ... }\\") + averageRating_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { gte: ... }\\") + averageRating_IN: [Float] @deprecated(reason: \\"Please use the relevant generic filter averageRating: { in: ... }\\") + averageRating_LT: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lt: ... }\\") + averageRating_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter averageRating: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + isActive: BooleanScalarFilters + isActive_EQ: Boolean @deprecated(reason: \\"Please use the relevant generic filter isActive: { eq: ... }\\") } type MoviesConnection { @@ -4316,9 +4462,9 @@ describe("Subscriptions", () => { } type Person { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): PersonMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): PersonMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! } type PersonAggregateSelection { @@ -4368,14 +4514,13 @@ describe("Subscriptions", () => { type PersonMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input PersonMoviesAggregateInput { AND: [PersonMoviesAggregateInput!] NOT: PersonMoviesAggregateInput OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4386,10 +4531,6 @@ describe("Subscriptions", () => { input PersonMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -4399,15 +4540,34 @@ describe("Subscriptions", () => { totalCount: Int! } - input PersonMoviesConnectionSort { - node: MovieSort - } - - input PersonMoviesConnectionWhere { - AND: [PersonMoviesConnectionWhere!] - NOT: PersonMoviesConnectionWhere - OR: [PersonMoviesConnectionWhere!] - node: MovieWhere + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + all: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + none: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + single: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + some: PersonMoviesConnectionWhere + } + + input PersonMoviesConnectionSort { + node: MovieSort + } + + input PersonMoviesConnectionWhere { + AND: [PersonMoviesConnectionWhere!] + NOT: PersonMoviesConnectionWhere + OR: [PersonMoviesConnectionWhere!] + node: MovieWhere } input PersonMoviesCreateFieldInput { @@ -4433,56 +4593,48 @@ describe("Subscriptions", () => { AND: [PersonMoviesNodeAggregationWhereInput!] NOT: PersonMoviesNodeAggregationWhereInput OR: [PersonMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type PersonMoviesRelationship { @@ -4503,11 +4655,6 @@ describe("Subscriptions", () => { where: PersonMoviesConnectionWhere } - input PersonOptions { - limit: Int - offset: Int - } - input PersonUpdateInput { movies: [PersonMoviesUpdateFieldInput!] } @@ -4521,52 +4668,48 @@ describe("Subscriptions", () => { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] + movies: MovieRelationshipFilters moviesAggregate: PersonMoviesAggregateInput + moviesConnection: PersonMoviesConnectionFilters \\"\\"\\" Return People where all of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: PersonMoviesConnectionWhere + moviesConnection_ALL: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: PersonMoviesConnectionWhere + moviesConnection_NONE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: PersonMoviesConnectionWhere + moviesConnection_SINGLE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: PersonMoviesConnectionWhere + moviesConnection_SOME: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type Query { - actors(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: ActorWhere): [Actor!]! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + actors(limit: Int, offset: Int, where: ActorWhere): [Actor!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, where: PersonWhere): PeopleConnection! - stars(limit: Int, offset: Int, options: StarOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: StarWhere): [Star!]! + stars(limit: Int, offset: Int, where: StarWhere): [Star!]! starsAggregate(where: StarWhere): StarAggregateSelection! starsConnection(after: String, first: Int, where: StarWhere): StarsConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" enum SortDirection { \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" @@ -4576,9 +4719,9 @@ describe("Subscriptions", () => { } type Star { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): StarMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [StarMoviesConnectionSort!], where: StarMoviesConnectionWhere): StarMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): StarMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [StarMoviesConnectionSort!], where: StarMoviesConnectionWhere): StarMoviesConnection! } type StarAggregateSelection { @@ -4618,14 +4761,13 @@ describe("Subscriptions", () => { type StarMovieMoviesNodeAggregateSelection { actorCount: IntAggregateSelection! averageRating: FloatAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input StarMoviesAggregateInput { AND: [StarMoviesAggregateInput!] NOT: StarMoviesAggregateInput OR: [StarMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -4636,10 +4778,6 @@ describe("Subscriptions", () => { input StarMoviesConnectFieldInput { connect: [MovieConnectInput!] - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } @@ -4649,6 +4787,25 @@ describe("Subscriptions", () => { totalCount: Int! } + input StarMoviesConnectionFilters { + \\"\\"\\" + Return Stars where all of the related StarMoviesConnections match this filter + \\"\\"\\" + all: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where none of the related StarMoviesConnections match this filter + \\"\\"\\" + none: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where one of the related StarMoviesConnections match this filter + \\"\\"\\" + single: StarMoviesConnectionWhere + \\"\\"\\" + Return Stars where some of the related StarMoviesConnections match this filter + \\"\\"\\" + some: StarMoviesConnectionWhere + } + input StarMoviesConnectionSort { node: MovieSort } @@ -4683,56 +4840,48 @@ describe("Subscriptions", () => { AND: [StarMoviesNodeAggregationWhereInput!] NOT: StarMoviesNodeAggregationWhereInput OR: [StarMoviesNodeAggregationWhereInput!] - actorCount_AVERAGE_EQUAL: Float - actorCount_AVERAGE_GT: Float - actorCount_AVERAGE_GTE: Float - actorCount_AVERAGE_LT: Float - actorCount_AVERAGE_LTE: Float - actorCount_MAX_EQUAL: Int - actorCount_MAX_GT: Int - actorCount_MAX_GTE: Int - actorCount_MAX_LT: Int - actorCount_MAX_LTE: Int - actorCount_MIN_EQUAL: Int - actorCount_MIN_GT: Int - actorCount_MIN_GTE: Int - actorCount_MIN_LT: Int - actorCount_MIN_LTE: Int - actorCount_SUM_EQUAL: Int - actorCount_SUM_GT: Int - actorCount_SUM_GTE: Int - actorCount_SUM_LT: Int - actorCount_SUM_LTE: Int - averageRating_AVERAGE_EQUAL: Float - averageRating_AVERAGE_GT: Float - averageRating_AVERAGE_GTE: Float - averageRating_AVERAGE_LT: Float - averageRating_AVERAGE_LTE: Float - averageRating_MAX_EQUAL: Float - averageRating_MAX_GT: Float - averageRating_MAX_GTE: Float - averageRating_MAX_LT: Float - averageRating_MAX_LTE: Float - averageRating_MIN_EQUAL: Float - averageRating_MIN_GT: Float - averageRating_MIN_GTE: Float - averageRating_MIN_LT: Float - averageRating_MIN_LTE: Float - averageRating_SUM_EQUAL: Float - averageRating_SUM_GT: Float - averageRating_SUM_GTE: Float - averageRating_SUM_LT: Float - averageRating_SUM_LTE: Float - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") + actorCount: IntScalarAggregationFilters + actorCount_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { eq: ... } } }' instead.\\") + actorCount_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gt: ... } } }' instead.\\") + actorCount_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { gte: ... } } }' instead.\\") + actorCount_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lt: ... } } }' instead.\\") + actorCount_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { average: { lte: ... } } }' instead.\\") + actorCount_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { eq: ... } } }' instead.\\") + actorCount_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gt: ... } } }' instead.\\") + actorCount_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { gte: ... } } }' instead.\\") + actorCount_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lt: ... } } }' instead.\\") + actorCount_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { max: { lte: ... } } }' instead.\\") + actorCount_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { eq: ... } } }' instead.\\") + actorCount_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gt: ... } } }' instead.\\") + actorCount_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { gte: ... } } }' instead.\\") + actorCount_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lt: ... } } }' instead.\\") + actorCount_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { min: { lte: ... } } }' instead.\\") + actorCount_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { eq: ... } } }' instead.\\") + actorCount_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gt: ... } } }' instead.\\") + actorCount_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { gte: ... } } }' instead.\\") + actorCount_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lt: ... } } }' instead.\\") + actorCount_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'actorCount: { sum: { lte: ... } } }' instead.\\") + averageRating: FloatScalarAggregationFilters + averageRating_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { eq: ... } } }' instead.\\") + averageRating_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gt: ... } } }' instead.\\") + averageRating_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { gte: ... } } }' instead.\\") + averageRating_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lt: ... } } }' instead.\\") + averageRating_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { average: { lte: ... } } }' instead.\\") + averageRating_MAX_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { eq: ... } } }' instead.\\") + averageRating_MAX_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gt: ... } } }' instead.\\") + averageRating_MAX_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { gte: ... } } }' instead.\\") + averageRating_MAX_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lt: ... } } }' instead.\\") + averageRating_MAX_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { max: { lte: ... } } }' instead.\\") + averageRating_MIN_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { eq: ... } } }' instead.\\") + averageRating_MIN_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gt: ... } } }' instead.\\") + averageRating_MIN_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { gte: ... } } }' instead.\\") + averageRating_MIN_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lt: ... } } }' instead.\\") + averageRating_MIN_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { min: { lte: ... } } }' instead.\\") + averageRating_SUM_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { eq: ... } } }' instead.\\") + averageRating_SUM_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gt: ... } } }' instead.\\") + averageRating_SUM_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { gte: ... } } }' instead.\\") + averageRating_SUM_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lt: ... } } }' instead.\\") + averageRating_SUM_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'averageRating: { sum: { lte: ... } } }' instead.\\") } type StarMoviesRelationship { @@ -4753,11 +4902,6 @@ describe("Subscriptions", () => { where: StarMoviesConnectionWhere } - input StarOptions { - limit: Int - offset: Int - } - input StarUpdateInput { movies: [StarMoviesUpdateFieldInput!] } @@ -4766,31 +4910,33 @@ describe("Subscriptions", () => { AND: [StarWhere!] NOT: StarWhere OR: [StarWhere!] + movies: MovieRelationshipFilters moviesAggregate: StarMoviesAggregateInput + moviesConnection: StarMoviesConnectionFilters \\"\\"\\" Return Stars where all of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: StarMoviesConnectionWhere + moviesConnection_ALL: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where none of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: StarMoviesConnectionWhere + moviesConnection_NONE: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where one of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: StarMoviesConnectionWhere + moviesConnection_SINGLE: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Stars where some of the related StarMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: StarMoviesConnectionWhere + moviesConnection_SOME: StarMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Stars where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Stars where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Stars where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Stars where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") } type StarsConnection { @@ -4834,953 +4980,4 @@ describe("Subscriptions", () => { }" `); }); - - test("Type with relationship to a subscriptions excluded type + Interface type", async () => { - const typeDefs = gql` - type Movie implements Production @subscription(events: []) @node { - title: String! - id: ID @unique - director: Creature! @relationship(type: "DIRECTED", direction: IN) - } - - type Series implements Production @node { - title: String! - episode: Int! - id: ID @unique - director: Creature! @relationship(type: "DIRECTED", direction: IN) - } - - interface Production { - id: ID - director: Creature! @declareRelationship - } - - type Person implements Creature @node { - movies: Production! @relationship(type: "DIRECTED", direction: OUT) - } - - interface Creature { - movies: Production! @declareRelationship - } - `; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - subscriptions: new TestCDCEngine(), - }, - }); - - const printedSchema = printSchemaWithDirectives(lexicographicSortSchema(await neoSchema.getSchema())); - - expect(printedSchema).toMatchInlineSnapshot(` - "schema { - query: Query - mutation: Mutation - subscription: Subscription - } - - \\"\\"\\" - Information about the number of nodes and relationships created during a create mutation - \\"\\"\\" - type CreateInfo { - nodesCreated: Int! - relationshipsCreated: Int! - } - - type CreateMoviesMutationResponse { - info: CreateInfo! - movies: [Movie!]! - } - - type CreatePeopleMutationResponse { - info: CreateInfo! - people: [Person!]! - } - - type CreateSeriesMutationResponse { - info: CreateInfo! - series: [Series!]! - } - - interface Creature { - movies(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! - moviesConnection(after: String, first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! - } - - type CreatureAggregateSelection { - count: Int! - } - - input CreatureConnectInput { - movies: CreatureMoviesConnectFieldInput - } - - input CreatureConnectWhere { - node: CreatureWhere! - } - - input CreatureCreateInput { - Person: PersonCreateInput - } - - input CreatureDeleteInput { - movies: CreatureMoviesDeleteFieldInput - } - - input CreatureDisconnectInput { - movies: CreatureMoviesDisconnectFieldInput - } - - type CreatureEdge { - cursor: String! - node: Creature! - } - - enum CreatureImplementation { - Person - } - - input CreatureMoviesAggregateInput { - AND: [CreatureMoviesAggregateInput!] - NOT: CreatureMoviesAggregateInput - OR: [CreatureMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: CreatureMoviesNodeAggregationWhereInput - } - - input CreatureMoviesConnectFieldInput { - connect: ProductionConnectInput - where: ProductionConnectWhere - } - - type CreatureMoviesConnection { - edges: [CreatureMoviesRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input CreatureMoviesConnectionSort { - node: ProductionSort - } - - input CreatureMoviesConnectionWhere { - AND: [CreatureMoviesConnectionWhere!] - NOT: CreatureMoviesConnectionWhere - OR: [CreatureMoviesConnectionWhere!] - node: ProductionWhere - } - - input CreatureMoviesCreateFieldInput { - node: ProductionCreateInput! - } - - input CreatureMoviesDeleteFieldInput { - delete: ProductionDeleteInput - where: CreatureMoviesConnectionWhere - } - - input CreatureMoviesDisconnectFieldInput { - disconnect: ProductionDisconnectInput - where: CreatureMoviesConnectionWhere - } - - input CreatureMoviesNodeAggregationWhereInput { - AND: [CreatureMoviesNodeAggregationWhereInput!] - NOT: CreatureMoviesNodeAggregationWhereInput - OR: [CreatureMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - type CreatureMoviesRelationship { - cursor: String! - node: Production! - } - - input CreatureMoviesUpdateConnectionInput { - node: ProductionUpdateInput - } - - input CreatureMoviesUpdateFieldInput { - connect: CreatureMoviesConnectFieldInput - create: CreatureMoviesCreateFieldInput - delete: CreatureMoviesDeleteFieldInput - disconnect: CreatureMoviesDisconnectFieldInput - update: CreatureMoviesUpdateConnectionInput - where: CreatureMoviesConnectionWhere - } - - input CreatureOptions { - limit: Int - offset: Int - } - - input CreatureUpdateInput { - movies: CreatureMoviesUpdateFieldInput - } - - input CreatureWhere { - AND: [CreatureWhere!] - NOT: CreatureWhere - OR: [CreatureWhere!] - movies: ProductionWhere - moviesAggregate: CreatureMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere - typename: [CreatureImplementation!] - typename_IN: [CreatureImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type CreaturesConnection { - edges: [CreatureEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - \\"\\"\\" - Information about the number of nodes and relationships deleted during a delete mutation - \\"\\"\\" - type DeleteInfo { - nodesDeleted: Int! - relationshipsDeleted: Int! - } - - enum EventType { - CREATE - CREATE_RELATIONSHIP - DELETE - DELETE_RELATIONSHIP - UPDATE - } - - type IDAggregateSelection { - longest: ID - shortest: ID - } - - type IntAggregateSelection { - average: Float - max: Int - min: Int - sum: Int - } - - type Movie implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): MovieCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! - id: ID - title: String! - } - - type MovieAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - input MovieCreateInput { - director: MovieDirectorFieldInput - id: ID - title: String! - } - - type MovieCreatureDirectorAggregationSelection { - count: Int! - } - - input MovieDeleteInput { - director: MovieDirectorDeleteFieldInput - } - - input MovieDirectorAggregateInput { - AND: [MovieDirectorAggregateInput!] - NOT: MovieDirectorAggregateInput - OR: [MovieDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - } - - input MovieDirectorConnectFieldInput { - connect: CreatureConnectInput - where: CreatureConnectWhere - } - - input MovieDirectorCreateFieldInput { - node: CreatureCreateInput! - } - - input MovieDirectorDeleteFieldInput { - delete: CreatureDeleteInput - where: ProductionDirectorConnectionWhere - } - - input MovieDirectorDisconnectFieldInput { - disconnect: CreatureDisconnectInput - where: ProductionDirectorConnectionWhere - } - - input MovieDirectorFieldInput { - connect: MovieDirectorConnectFieldInput - create: MovieDirectorCreateFieldInput - } - - input MovieDirectorUpdateConnectionInput { - node: CreatureUpdateInput - } - - input MovieDirectorUpdateFieldInput { - connect: MovieDirectorConnectFieldInput - create: MovieDirectorCreateFieldInput - delete: MovieDirectorDeleteFieldInput - disconnect: MovieDirectorDisconnectFieldInput - update: MovieDirectorUpdateConnectionInput - where: ProductionDirectorConnectionWhere - } - - type MovieEdge { - cursor: String! - node: Movie! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - id: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - director: MovieDirectorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - input MovieWhere { - AND: [MovieWhere!] - NOT: MovieWhere - OR: [MovieWhere!] - director: CreatureWhere - directorAggregate: MovieDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - type MoviesConnection { - edges: [MovieEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Mutation { - createMovies(input: [MovieCreateInput!]!): CreateMoviesMutationResponse! - createPeople(input: [PersonCreateInput!]!): CreatePeopleMutationResponse! - createSeries(input: [SeriesCreateInput!]!): CreateSeriesMutationResponse! - deleteMovies(delete: MovieDeleteInput, where: MovieWhere): DeleteInfo! - deletePeople(delete: PersonDeleteInput, where: PersonWhere): DeleteInfo! - deleteSeries(delete: SeriesDeleteInput, where: SeriesWhere): DeleteInfo! - updateMovies(update: MovieUpdateInput, where: MovieWhere): UpdateMoviesMutationResponse! - updatePeople(update: PersonUpdateInput, where: PersonWhere): UpdatePeopleMutationResponse! - updateSeries(update: SeriesUpdateInput, where: SeriesWhere): UpdateSeriesMutationResponse! - } - - \\"\\"\\"Pagination information (Relay)\\"\\"\\" - type PageInfo { - endCursor: String - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - } - - type PeopleConnection { - edges: [PersonEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Person implements Creature { - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): Production! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ProductionWhere): PersonProductionMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [CreatureMoviesConnectionSort!], where: CreatureMoviesConnectionWhere): CreatureMoviesConnection! - } - - type PersonAggregateSelection { - count: Int! - } - - input PersonCreateInput { - movies: PersonMoviesFieldInput - } - - type PersonCreatedEvent { - event: EventType! - timestamp: Float! - } - - input PersonDeleteInput { - movies: PersonMoviesDeleteFieldInput - } - - type PersonDeletedEvent { - event: EventType! - timestamp: Float! - } - - type PersonEdge { - cursor: String! - node: Person! - } - - input PersonMoviesAggregateInput { - AND: [PersonMoviesAggregateInput!] - NOT: PersonMoviesAggregateInput - OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - node: PersonMoviesNodeAggregationWhereInput - } - - input PersonMoviesConnectFieldInput { - connect: ProductionConnectInput - where: ProductionConnectWhere - } - - input PersonMoviesCreateFieldInput { - node: ProductionCreateInput! - } - - input PersonMoviesDeleteFieldInput { - delete: ProductionDeleteInput - where: CreatureMoviesConnectionWhere - } - - input PersonMoviesDisconnectFieldInput { - disconnect: ProductionDisconnectInput - where: CreatureMoviesConnectionWhere - } - - input PersonMoviesFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - } - - input PersonMoviesNodeAggregationWhereInput { - AND: [PersonMoviesNodeAggregationWhereInput!] - NOT: PersonMoviesNodeAggregationWhereInput - OR: [PersonMoviesNodeAggregationWhereInput!] - id_MAX_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MAX_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_EQUAL: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_GTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LT: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - id_MIN_LTE: ID @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input PersonMoviesUpdateConnectionInput { - node: ProductionUpdateInput - } - - input PersonMoviesUpdateFieldInput { - connect: PersonMoviesConnectFieldInput - create: PersonMoviesCreateFieldInput - delete: PersonMoviesDeleteFieldInput - disconnect: PersonMoviesDisconnectFieldInput - update: PersonMoviesUpdateConnectionInput - where: CreatureMoviesConnectionWhere - } - - input PersonOptions { - limit: Int - offset: Int - } - - type PersonProductionMoviesAggregationSelection { - count: Int! - node: PersonProductionMoviesNodeAggregateSelection - } - - type PersonProductionMoviesNodeAggregateSelection { - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input PersonUpdateInput { - movies: PersonMoviesUpdateFieldInput - } - - type PersonUpdatedEvent { - event: EventType! - timestamp: Float! - } - - input PersonWhere { - AND: [PersonWhere!] - NOT: PersonWhere - OR: [PersonWhere!] - movies: ProductionWhere - moviesAggregate: PersonMoviesAggregateInput - moviesConnection: CreatureMoviesConnectionWhere - } - - interface Production { - director(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! - directorConnection(after: String, first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! - id: ID - } - - type ProductionAggregateSelection { - count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - } - - input ProductionConnectInput { - director: ProductionDirectorConnectFieldInput - } - - input ProductionConnectWhere { - node: ProductionWhere! - } - - input ProductionCreateInput { - Movie: MovieCreateInput - Series: SeriesCreateInput - } - - input ProductionDeleteInput { - director: ProductionDirectorDeleteFieldInput - } - - input ProductionDirectorAggregateInput { - AND: [ProductionDirectorAggregateInput!] - NOT: ProductionDirectorAggregateInput - OR: [ProductionDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - } - - input ProductionDirectorConnectFieldInput { - connect: CreatureConnectInput - where: CreatureConnectWhere - } - - type ProductionDirectorConnection { - edges: [ProductionDirectorRelationship!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input ProductionDirectorConnectionWhere { - AND: [ProductionDirectorConnectionWhere!] - NOT: ProductionDirectorConnectionWhere - OR: [ProductionDirectorConnectionWhere!] - node: CreatureWhere - } - - input ProductionDirectorCreateFieldInput { - node: CreatureCreateInput! - } - - input ProductionDirectorDeleteFieldInput { - delete: CreatureDeleteInput - where: ProductionDirectorConnectionWhere - } - - input ProductionDirectorDisconnectFieldInput { - disconnect: CreatureDisconnectInput - where: ProductionDirectorConnectionWhere - } - - type ProductionDirectorRelationship { - cursor: String! - node: Creature! - } - - input ProductionDirectorUpdateConnectionInput { - node: CreatureUpdateInput - } - - input ProductionDirectorUpdateFieldInput { - connect: ProductionDirectorConnectFieldInput - create: ProductionDirectorCreateFieldInput - delete: ProductionDirectorDeleteFieldInput - disconnect: ProductionDirectorDisconnectFieldInput - update: ProductionDirectorUpdateConnectionInput - where: ProductionDirectorConnectionWhere - } - - input ProductionDisconnectInput { - director: ProductionDirectorDisconnectFieldInput - } - - type ProductionEdge { - cursor: String! - node: Production! - } - - enum ProductionImplementation { - Movie - Series - } - - input ProductionOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ProductionSort objects to sort Productions by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ProductionSort!] - } - - \\"\\"\\" - Fields to sort Productions by. The order in which sorts are applied is not guaranteed when specifying many fields in one ProductionSort object. - \\"\\"\\" - input ProductionSort { - id: SortDirection - } - - input ProductionUpdateInput { - director: ProductionDirectorUpdateFieldInput - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - } - - input ProductionWhere { - AND: [ProductionWhere!] - NOT: ProductionWhere - OR: [ProductionWhere!] - director: CreatureWhere - directorAggregate: ProductionDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - typename: [ProductionImplementation!] - typename_IN: [ProductionImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") - } - - type ProductionsConnection { - edges: [ProductionEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - type Query { - creatures(limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): [Creature!]! - creaturesAggregate(where: CreatureWhere): CreatureAggregateSelection! - creaturesConnection(after: String, first: Int, where: CreatureWhere): CreaturesConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(where: MovieWhere): MovieAggregateSelection! - moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: PersonWhere): [Person!]! - peopleAggregate(where: PersonWhere): PersonAggregateSelection! - peopleConnection(after: String, first: Int, where: PersonWhere): PeopleConnection! - productions(limit: Int, offset: Int, options: ProductionOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ProductionSort!], where: ProductionWhere): [Production!]! - productionsAggregate(where: ProductionWhere): ProductionAggregateSelection! - productionsConnection(after: String, first: Int, sort: [ProductionSort!], where: ProductionWhere): ProductionsConnection! - series(limit: Int, offset: Int, options: SeriesOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [SeriesSort!], where: SeriesWhere): [Series!]! - seriesAggregate(where: SeriesWhere): SeriesAggregateSelection! - seriesConnection(after: String, first: Int, sort: [SeriesSort!], where: SeriesWhere): SeriesConnection! - } - - type Series implements Production { - director(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: CreatureOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: CreatureWhere): Creature! - directorAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: CreatureWhere): SeriesCreatureDirectorAggregationSelection - directorConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: ProductionDirectorConnectionWhere): ProductionDirectorConnection! - episode: Int! - id: ID - title: String! - } - - type SeriesAggregateSelection { - count: Int! - episode: IntAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") - title: StringAggregateSelection! - } - - type SeriesConnection { - edges: [SeriesEdge!]! - pageInfo: PageInfo! - totalCount: Int! - } - - input SeriesCreateInput { - director: SeriesDirectorFieldInput - episode: Int! - id: ID - title: String! - } - - type SeriesCreatedEvent { - createdSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - type SeriesCreatureDirectorAggregationSelection { - count: Int! - } - - input SeriesDeleteInput { - director: SeriesDirectorDeleteFieldInput - } - - type SeriesDeletedEvent { - deletedSeries: SeriesEventPayload! - event: EventType! - timestamp: Float! - } - - input SeriesDirectorAggregateInput { - AND: [SeriesDirectorAggregateInput!] - NOT: SeriesDirectorAggregateInput - OR: [SeriesDirectorAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - count_EQ: Int - count_GT: Int - count_GTE: Int - count_LT: Int - count_LTE: Int - } - - input SeriesDirectorConnectFieldInput { - connect: CreatureConnectInput - where: CreatureConnectWhere - } - - input SeriesDirectorCreateFieldInput { - node: CreatureCreateInput! - } - - input SeriesDirectorDeleteFieldInput { - delete: CreatureDeleteInput - where: ProductionDirectorConnectionWhere - } - - input SeriesDirectorDisconnectFieldInput { - disconnect: CreatureDisconnectInput - where: ProductionDirectorConnectionWhere - } - - input SeriesDirectorFieldInput { - connect: SeriesDirectorConnectFieldInput - create: SeriesDirectorCreateFieldInput - } - - input SeriesDirectorUpdateConnectionInput { - node: CreatureUpdateInput - } - - input SeriesDirectorUpdateFieldInput { - connect: SeriesDirectorConnectFieldInput - create: SeriesDirectorCreateFieldInput - delete: SeriesDirectorDeleteFieldInput - disconnect: SeriesDirectorDisconnectFieldInput - update: SeriesDirectorUpdateConnectionInput - where: ProductionDirectorConnectionWhere - } - - type SeriesEdge { - cursor: String! - node: Series! - } - - type SeriesEventPayload { - episode: Int! - id: ID - title: String! - } - - input SeriesOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more SeriesSort objects to sort Series by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [SeriesSort!] - } - - \\"\\"\\" - Fields to sort Series by. The order in which sorts are applied is not guaranteed when specifying many fields in one SeriesSort object. - \\"\\"\\" - input SeriesSort { - episode: SortDirection - id: SortDirection - title: SortDirection - } - - input SeriesSubscriptionWhere { - AND: [SeriesSubscriptionWhere!] - NOT: SeriesSubscriptionWhere - OR: [SeriesSubscriptionWhere!] - episode: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episode_EQ: Int - episode_GT: Int - episode_GTE: Int - episode_IN: [Int!] - episode_LT: Int - episode_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - input SeriesUpdateInput { - director: SeriesDirectorUpdateFieldInput - episode: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - episode_DECREMENT: Int - episode_INCREMENT: Int - episode_SET: Int - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - type SeriesUpdatedEvent { - event: EventType! - previousState: SeriesEventPayload! - timestamp: Float! - updatedSeries: SeriesEventPayload! - } - - input SeriesWhere { - AND: [SeriesWhere!] - NOT: SeriesWhere - OR: [SeriesWhere!] - director: CreatureWhere - directorAggregate: SeriesDirectorAggregateInput - directorConnection: ProductionDirectorConnectionWhere - episode: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - episode_EQ: Int - episode_GT: Int - episode_GTE: Int - episode_IN: [Int!] - episode_LT: Int - episode_LTE: Int - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String - } - - \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" - enum SortDirection { - \\"\\"\\"Sort by field values in ascending order.\\"\\"\\" - ASC - \\"\\"\\"Sort by field values in descending order.\\"\\"\\" - DESC - } - - type StringAggregateSelection { - longest: String - shortest: String - } - - type Subscription { - personCreated: PersonCreatedEvent! - personDeleted: PersonDeletedEvent! - personUpdated: PersonUpdatedEvent! - seriesCreated(where: SeriesSubscriptionWhere): SeriesCreatedEvent! - seriesDeleted(where: SeriesSubscriptionWhere): SeriesDeletedEvent! - seriesUpdated(where: SeriesSubscriptionWhere): SeriesUpdatedEvent! - } - - \\"\\"\\" - Information about the number of nodes and relationships created and deleted during an update mutation - \\"\\"\\" - type UpdateInfo { - nodesCreated: Int! - nodesDeleted: Int! - relationshipsCreated: Int! - relationshipsDeleted: Int! - } - - type UpdateMoviesMutationResponse { - info: UpdateInfo! - movies: [Movie!]! - } - - type UpdatePeopleMutationResponse { - info: UpdateInfo! - people: [Person!]! - } - - type UpdateSeriesMutationResponse { - info: UpdateInfo! - series: [Series!]! - }" - `); - }); }); diff --git a/packages/graphql/tests/schema/types/bigint.test.ts b/packages/graphql/tests/schema/types/bigint.test.ts index cfa692bb60..48fd45ceca 100644 --- a/packages/graphql/tests/schema/types/bigint.test.ts +++ b/packages/graphql/tests/schema/types/bigint.test.ts @@ -51,6 +51,23 @@ describe("Bigint", () => { sum: BigInt } + \\"\\"\\"BigInt filters\\"\\"\\" + input BigIntScalarFilters { + eq: BigInt + gt: BigInt + gte: BigInt + in: [BigInt!] + lt: BigInt + lte: BigInt + } + + \\"\\"\\"BigInt mutations\\"\\"\\" + input BigIntScalarMutations { + add: BigInt + set: BigInt + subtract: BigInt + } + type CreateFilesMutationResponse { files: [File!]! info: CreateInfo! @@ -93,15 +110,6 @@ describe("Bigint", () => { node: File! } - input FileOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more FileSort objects to sort Files by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [FileSort!] - } - \\"\\"\\" Fields to sort Files by. The order in which sorts are applied is not guaranteed when specifying many fields in one FileSort object. \\"\\"\\" @@ -111,31 +119,31 @@ describe("Bigint", () => { } input FileUpdateInput { - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - size: BigInt @deprecated(reason: \\"Please use the explicit _SET field\\") - size_DECREMENT: BigInt - size_INCREMENT: BigInt - size_SET: BigInt + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + size: BigIntScalarMutations + size_DECREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'size: { decrement: ... } }' instead.\\") + size_INCREMENT: BigInt @deprecated(reason: \\"Please use the relevant generic mutation 'size: { increment: ... } }' instead.\\") + size_SET: BigInt @deprecated(reason: \\"Please use the generic mutation 'size: { set: ... } }' instead.\\") } input FileWhere { AND: [FileWhere!] NOT: FileWhere OR: [FileWhere!] - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - size: BigInt @deprecated(reason: \\"Please use the explicit _EQ version\\") - size_EQ: BigInt - size_GT: BigInt - size_GTE: BigInt - size_IN: [BigInt!] - size_LT: BigInt - size_LTE: BigInt + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + size: BigIntScalarFilters + size_EQ: BigInt @deprecated(reason: \\"Please use the relevant generic filter size: { eq: ... }\\") + size_GT: BigInt @deprecated(reason: \\"Please use the relevant generic filter size: { gt: ... }\\") + size_GTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter size: { gte: ... }\\") + size_IN: [BigInt!] @deprecated(reason: \\"Please use the relevant generic filter size: { in: ... }\\") + size_LT: BigInt @deprecated(reason: \\"Please use the relevant generic filter size: { lt: ... }\\") + size_LTE: BigInt @deprecated(reason: \\"Please use the relevant generic filter size: { lte: ... }\\") } type FilesConnection { @@ -159,7 +167,7 @@ describe("Bigint", () => { } type Query { - files(limit: Int, offset: Int, options: FileOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [FileSort!], where: FileWhere): [File!]! + files(limit: Int, offset: Int, sort: [FileSort!], where: FileWhere): [File!]! filesAggregate(where: FileWhere): FileAggregateSelection! filesConnection(after: String, first: Int, sort: [FileSort!], where: FileWhere): FilesConnection! } @@ -177,6 +185,20 @@ describe("Bigint", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateFilesMutationResponse { files: [File!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/types/date.test.ts b/packages/graphql/tests/schema/types/date.test.ts index af7ea0876e..5c31c7f9f4 100644 --- a/packages/graphql/tests/schema/types/date.test.ts +++ b/packages/graphql/tests/schema/types/date.test.ts @@ -55,6 +55,21 @@ describe("Date", () => { \\"\\"\\"A date, represented as a 'yyyy-mm-dd' string\\"\\"\\" scalar Date + \\"\\"\\"Date filters\\"\\"\\" + input DateScalarFilters { + eq: Date + gt: Date + gte: Date + in: [Date!] + lt: Date + lte: Date + } + + \\"\\"\\"Date mutations\\"\\"\\" + input DateScalarMutations { + set: Date + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -63,9 +78,18 @@ describe("Date", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -75,7 +99,6 @@ describe("Date", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -88,15 +111,6 @@ describe("Date", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -106,29 +120,29 @@ describe("Date", () => { } input MovieUpdateInput { - date: Date @deprecated(reason: \\"Please use the explicit _SET field\\") - date_SET: Date - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + date: DateScalarMutations + date_SET: Date @deprecated(reason: \\"Please use the generic mutation 'date: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - date: Date @deprecated(reason: \\"Please use the explicit _EQ version\\") - date_EQ: Date - date_GT: Date - date_GTE: Date - date_IN: [Date] - date_LT: Date - date_LTE: Date - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + date: DateScalarFilters + date_EQ: Date @deprecated(reason: \\"Please use the relevant generic filter date: { eq: ... }\\") + date_GT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gt: ... }\\") + date_GTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { gte: ... }\\") + date_IN: [Date] @deprecated(reason: \\"Please use the relevant generic filter date: { in: ... }\\") + date_LT: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lt: ... }\\") + date_LTE: Date @deprecated(reason: \\"Please use the relevant generic filter date: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -152,7 +166,7 @@ describe("Date", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/types/datetime.test.ts b/packages/graphql/tests/schema/types/datetime.test.ts index 0932a7c88e..7b2acdc58b 100644 --- a/packages/graphql/tests/schema/types/datetime.test.ts +++ b/packages/graphql/tests/schema/types/datetime.test.ts @@ -60,6 +60,21 @@ describe("Datetime", () => { min: DateTime } + \\"\\"\\"DateTime filters\\"\\"\\" + input DateTimeScalarFilters { + eq: DateTime + gt: DateTime + gte: DateTime + in: [DateTime!] + lt: DateTime + lte: DateTime + } + + \\"\\"\\"DateTime mutations\\"\\"\\" + input DateTimeScalarMutations { + set: DateTime + } + \\"\\"\\" Information about the number of nodes and relationships deleted during a delete mutation \\"\\"\\" @@ -68,9 +83,18 @@ describe("Datetime", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -81,7 +105,6 @@ describe("Datetime", () => { type MovieAggregateSelection { count: Int! datetime: DateTimeAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -94,15 +117,6 @@ describe("Datetime", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -112,29 +126,29 @@ describe("Datetime", () => { } input MovieUpdateInput { - datetime: DateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - datetime_SET: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + datetime: DateTimeScalarMutations + datetime_SET: DateTime @deprecated(reason: \\"Please use the generic mutation 'datetime: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - datetime: DateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - datetime_EQ: DateTime - datetime_GT: DateTime - datetime_GTE: DateTime - datetime_IN: [DateTime] - datetime_LT: DateTime - datetime_LTE: DateTime - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + datetime: DateTimeScalarFilters + datetime_EQ: DateTime @deprecated(reason: \\"Please use the relevant generic filter datetime: { eq: ... }\\") + datetime_GT: DateTime @deprecated(reason: \\"Please use the relevant generic filter datetime: { gt: ... }\\") + datetime_GTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter datetime: { gte: ... }\\") + datetime_IN: [DateTime] @deprecated(reason: \\"Please use the relevant generic filter datetime: { in: ... }\\") + datetime_LT: DateTime @deprecated(reason: \\"Please use the relevant generic filter datetime: { lt: ... }\\") + datetime_LTE: DateTime @deprecated(reason: \\"Please use the relevant generic filter datetime: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -158,7 +172,7 @@ describe("Datetime", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/types/duration.test.ts b/packages/graphql/tests/schema/types/duration.test.ts index 15d0333d9c..b829d161c1 100644 --- a/packages/graphql/tests/schema/types/duration.test.ts +++ b/packages/graphql/tests/schema/types/duration.test.ts @@ -68,9 +68,33 @@ describe("Duration", () => { min: Duration } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"Duration filters\\"\\"\\" + input DurationScalarFilters { + eq: Duration + gt: Duration + gte: Duration + in: [Duration!] + lt: Duration + lte: Duration + } + + \\"\\"\\"Duration mutations\\"\\"\\" + input DurationScalarMutations { + set: Duration + } + + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -81,7 +105,6 @@ describe("Duration", () => { type MovieAggregateSelection { count: Int! duration: DurationAggregateSelection! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieCreateInput { @@ -94,15 +117,6 @@ describe("Duration", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -112,29 +126,29 @@ describe("Duration", () => { } input MovieUpdateInput { - duration: Duration @deprecated(reason: \\"Please use the explicit _SET field\\") - duration_SET: Duration - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + duration: DurationScalarMutations + duration_SET: Duration @deprecated(reason: \\"Please use the generic mutation 'duration: { set: ... } }' instead.\\") + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - duration: Duration @deprecated(reason: \\"Please use the explicit _EQ version\\") - duration_EQ: Duration - duration_GT: Duration - duration_GTE: Duration - duration_IN: [Duration] - duration_LT: Duration - duration_LTE: Duration - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + duration: DurationScalarFilters + duration_EQ: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { eq: ... }\\") + duration_GT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gt: ... }\\") + duration_GTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { gte: ... }\\") + duration_IN: [Duration] @deprecated(reason: \\"Please use the relevant generic filter duration: { in: ... }\\") + duration_LT: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lt: ... }\\") + duration_LTE: Duration @deprecated(reason: \\"Please use the relevant generic filter duration: { lte: ... }\\") + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type MoviesConnection { @@ -158,7 +172,7 @@ describe("Duration", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/types/localdatetime.test.ts b/packages/graphql/tests/schema/types/localdatetime.test.ts index 74ebcae80e..3dfb8a2e38 100644 --- a/packages/graphql/tests/schema/types/localdatetime.test.ts +++ b/packages/graphql/tests/schema/types/localdatetime.test.ts @@ -60,9 +60,18 @@ describe("Localdatetime", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } \\"\\"\\"A local datetime, represented as 'YYYY-MM-DDTHH:MM:SS'\\"\\"\\" @@ -73,6 +82,21 @@ describe("Localdatetime", () => { min: LocalDateTime } + \\"\\"\\"LocalDateTime filters\\"\\"\\" + input LocalDateTimeScalarFilters { + eq: LocalDateTime + gt: LocalDateTime + gte: LocalDateTime + in: [LocalDateTime!] + lt: LocalDateTime + lte: LocalDateTime + } + + \\"\\"\\"LocalDateTime mutations\\"\\"\\" + input LocalDateTimeScalarMutations { + set: LocalDateTime + } + type Movie { id: ID localDT: LocalDateTime @@ -80,7 +104,6 @@ describe("Localdatetime", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") localDT: LocalDateTimeAggregateSelection! } @@ -94,15 +117,6 @@ describe("Localdatetime", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -112,29 +126,29 @@ describe("Localdatetime", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - localDT: LocalDateTime @deprecated(reason: \\"Please use the explicit _SET field\\") - localDT_SET: LocalDateTime + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + localDT: LocalDateTimeScalarMutations + localDT_SET: LocalDateTime @deprecated(reason: \\"Please use the generic mutation 'localDT: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - localDT: LocalDateTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - localDT_EQ: LocalDateTime - localDT_GT: LocalDateTime - localDT_GTE: LocalDateTime - localDT_IN: [LocalDateTime] - localDT_LT: LocalDateTime - localDT_LTE: LocalDateTime + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + localDT: LocalDateTimeScalarFilters + localDT_EQ: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDT: { eq: ... }\\") + localDT_GT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDT: { gt: ... }\\") + localDT_GTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDT: { gte: ... }\\") + localDT_IN: [LocalDateTime] @deprecated(reason: \\"Please use the relevant generic filter localDT: { in: ... }\\") + localDT_LT: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDT: { lt: ... }\\") + localDT_LTE: LocalDateTime @deprecated(reason: \\"Please use the relevant generic filter localDT: { lte: ... }\\") } type MoviesConnection { @@ -158,7 +172,7 @@ describe("Localdatetime", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/types/localtime.test.ts b/packages/graphql/tests/schema/types/localtime.test.ts index 8adf888974..951d8175a4 100644 --- a/packages/graphql/tests/schema/types/localtime.test.ts +++ b/packages/graphql/tests/schema/types/localtime.test.ts @@ -60,9 +60,18 @@ describe("Localtime", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } \\"\\"\\" @@ -75,6 +84,21 @@ describe("Localtime", () => { min: LocalTime } + \\"\\"\\"LocalTime filters\\"\\"\\" + input LocalTimeScalarFilters { + eq: LocalTime + gt: LocalTime + gte: LocalTime + in: [LocalTime!] + lt: LocalTime + lte: LocalTime + } + + \\"\\"\\"LocalTime mutations\\"\\"\\" + input LocalTimeScalarMutations { + set: LocalTime + } + type Movie { id: ID time: LocalTime @@ -82,7 +106,6 @@ describe("Localtime", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") time: LocalTimeAggregateSelection! } @@ -96,15 +119,6 @@ describe("Localtime", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -114,29 +128,29 @@ describe("Localtime", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - time: LocalTime @deprecated(reason: \\"Please use the explicit _SET field\\") - time_SET: LocalTime + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + time: LocalTimeScalarMutations + time_SET: LocalTime @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - time: LocalTime @deprecated(reason: \\"Please use the explicit _EQ version\\") - time_EQ: LocalTime - time_GT: LocalTime - time_GTE: LocalTime - time_IN: [LocalTime] - time_LT: LocalTime - time_LTE: LocalTime + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + time: LocalTimeScalarFilters + time_EQ: LocalTime @deprecated(reason: \\"Please use the relevant generic filter time: { eq: ... }\\") + time_GT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter time: { gt: ... }\\") + time_GTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter time: { gte: ... }\\") + time_IN: [LocalTime] @deprecated(reason: \\"Please use the relevant generic filter time: { in: ... }\\") + time_LT: LocalTime @deprecated(reason: \\"Please use the relevant generic filter time: { lt: ... }\\") + time_LTE: LocalTime @deprecated(reason: \\"Please use the relevant generic filter time: { lte: ... }\\") } type MoviesConnection { @@ -160,7 +174,7 @@ describe("Localtime", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } diff --git a/packages/graphql/tests/schema/types/point.test.ts b/packages/graphql/tests/schema/types/point.test.ts index dd2801aba1..323da4d94a 100644 --- a/packages/graphql/tests/schema/types/point.test.ts +++ b/packages/graphql/tests/schema/types/point.test.ts @@ -76,15 +76,6 @@ describe("Point", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -93,22 +84,22 @@ describe("Point", () => { } input MovieUpdateInput { - filmedAt: PointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - filmedAt_SET: PointInput + filmedAt: PointMutations + filmedAt_SET: PointInput @deprecated(reason: \\"Please use the generic mutation 'filmedAt: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - filmedAt: PointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - filmedAt_DISTANCE: PointDistance - filmedAt_EQ: PointInput - filmedAt_GT: PointDistance - filmedAt_GTE: PointDistance - filmedAt_IN: [PointInput!] - filmedAt_LT: PointDistance - filmedAt_LTE: PointDistance + filmedAt: PointFilters + filmedAt_DISTANCE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { distance: ... }\\") + filmedAt_EQ: PointInput @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { eq: ... }\\") + filmedAt_GT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { gt: ... }\\") + filmedAt_GTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { gte: ... }\\") + filmedAt_IN: [PointInput!] @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { in: ... }\\") + filmedAt_LT: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { lt: ... }\\") + filmedAt_LTE: PointDistance @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { lte: ... }\\") } type MoviesConnection { @@ -149,6 +140,23 @@ describe("Point", () => { point: PointInput! } + \\"\\"\\"Distance filters\\"\\"\\" + input PointDistanceFilters { + eq: Float + from: PointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + + \\"\\"\\"Point filters\\"\\"\\" + input PointFilters { + distance: PointDistanceFilters + eq: PointInput + in: [PointInput!] + } + \\"\\"\\"Input type for a point\\"\\"\\" input PointInput { height: Float @@ -156,8 +164,13 @@ describe("Point", () => { longitude: Float! } + \\"\\"\\"Point mutations\\"\\"\\" + input PointMutations { + set: PointInput + } + type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -202,6 +215,15 @@ describe("Point", () => { mutation: Mutation } + \\"\\"\\"Distance filters for cartesian points\\"\\"\\" + input CartesianDistancePointFilters { + from: CartesianPointInput! + gt: Float + gte: Float + lt: Float + lte: Float + } + \\"\\"\\" A point in a two- or three-dimensional Cartesian coordinate system or in a three-dimensional cylindrical coordinate system. For more information, see https://neo4j.com/docs/graphql/4/type-definitions/types/spatial/#cartesian-point \\"\\"\\" @@ -219,6 +241,13 @@ describe("Point", () => { point: CartesianPointInput! } + \\"\\"\\"Cartesian Point filters\\"\\"\\" + input CartesianPointFilters { + distance: CartesianDistancePointFilters + eq: CartesianPointInput + in: [CartesianPointInput!] + } + \\"\\"\\"Input type for a cartesian point\\"\\"\\" input CartesianPointInput { x: Float! @@ -226,6 +255,11 @@ describe("Point", () => { z: Float } + \\"\\"\\"CartesianPoint mutations\\"\\"\\" + input CartesianPointMutations { + set: CartesianPointInput + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -264,15 +298,6 @@ describe("Point", () => { node: Machine! } - input MachineOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MachineSort objects to sort Machines by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MachineSort!] - } - \\"\\"\\" Fields to sort Machines by. The order in which sorts are applied is not guaranteed when specifying many fields in one MachineSort object. \\"\\"\\" @@ -281,22 +306,22 @@ describe("Point", () => { } input MachineUpdateInput { - partLocation: CartesianPointInput @deprecated(reason: \\"Please use the explicit _SET field\\") - partLocation_SET: CartesianPointInput + partLocation: CartesianPointMutations + partLocation_SET: CartesianPointInput @deprecated(reason: \\"Please use the generic mutation 'partLocation: { set: ... } }' instead.\\") } input MachineWhere { AND: [MachineWhere!] NOT: MachineWhere OR: [MachineWhere!] - partLocation: CartesianPointInput @deprecated(reason: \\"Please use the explicit _EQ version\\") - partLocation_DISTANCE: CartesianPointDistance - partLocation_EQ: CartesianPointInput - partLocation_GT: CartesianPointDistance - partLocation_GTE: CartesianPointDistance - partLocation_IN: [CartesianPointInput!] - partLocation_LT: CartesianPointDistance - partLocation_LTE: CartesianPointDistance + partLocation: CartesianPointFilters + partLocation_DISTANCE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter partLocation: { distance: ... }\\") + partLocation_EQ: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter partLocation: { eq: ... }\\") + partLocation_GT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter partLocation: { gt: ... }\\") + partLocation_GTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter partLocation: { gte: ... }\\") + partLocation_IN: [CartesianPointInput!] @deprecated(reason: \\"Please use the relevant generic filter partLocation: { in: ... }\\") + partLocation_LT: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter partLocation: { lt: ... }\\") + partLocation_LTE: CartesianPointDistance @deprecated(reason: \\"Please use the relevant generic filter partLocation: { lte: ... }\\") } type MachinesConnection { @@ -320,7 +345,7 @@ describe("Point", () => { } type Query { - machines(limit: Int, offset: Int, options: MachineOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MachineSort!], where: MachineWhere): [Machine!]! + machines(limit: Int, offset: Int, sort: [MachineSort!], where: MachineWhere): [Machine!]! machinesAggregate(where: MachineWhere): MachineAggregateSelection! machinesConnection(after: String, first: Int, sort: [MachineSort!], where: MachineWhere): MachinesConnection! } @@ -386,6 +411,13 @@ describe("Point", () => { relationshipsDeleted: Int! } + \\"\\"\\"Mutations for a list for PointInput\\"\\"\\" + input ListPointInputMutations { + pop: Int + push: [PointInput!] + set: [PointInput!] + } + type Movie { filmedAt: [Point!]! } @@ -403,25 +435,20 @@ describe("Point", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - } - input MovieUpdateInput { - filmedAt: [PointInput!] @deprecated(reason: \\"Please use the explicit _SET field\\") - filmedAt_POP: Int - filmedAt_PUSH: [PointInput!] - filmedAt_SET: [PointInput!] + filmedAt: ListPointInputMutations + filmedAt_POP: Int @deprecated(reason: \\"Please use the generic mutation 'filmedAt: { pop: ... } }' instead.\\") + filmedAt_PUSH: [PointInput!] @deprecated(reason: \\"Please use the generic mutation 'filmedAt: { push: ... } }' instead.\\") + filmedAt_SET: [PointInput!] @deprecated(reason: \\"Please use the generic mutation 'filmedAt: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - filmedAt: [PointInput!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - filmedAt_EQ: [PointInput!] - filmedAt_INCLUDES: PointInput + filmedAt: PointListFilters + filmedAt_EQ: [PointInput!] @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { eq: ... }\\") + filmedAt_INCLUDES: PointInput @deprecated(reason: \\"Please use the relevant generic filter filmedAt: { includes: ... }\\") } type MoviesConnection { @@ -462,8 +489,14 @@ describe("Point", () => { longitude: Float! } + \\"\\"\\"Point list filters\\"\\"\\" + input PointListFilters { + eq: [PointInput!] + includes: PointInput + } + type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, where: MovieWhere): MoviesConnection! } @@ -518,6 +551,12 @@ describe("Point", () => { z: Float } + \\"\\"\\"CartesianPoint list filters\\"\\"\\" + input CartesianPointListFilters { + eq: [CartesianPointInput!] + includes: CartesianPointInput + } + \\"\\"\\" Information about the number of nodes and relationships created during a create mutation \\"\\"\\" @@ -539,6 +578,13 @@ describe("Point", () => { relationshipsDeleted: Int! } + \\"\\"\\"Mutations for a list for CartesianPointInput\\"\\"\\" + input ListCartesianPointInputMutations { + pop: Int + push: [CartesianPointInput!] + set: [CartesianPointInput!] + } + type Machine { partLocations: [CartesianPoint!]! } @@ -556,25 +602,20 @@ describe("Point", () => { node: Machine! } - input MachineOptions { - limit: Int - offset: Int - } - input MachineUpdateInput { - partLocations: [CartesianPointInput!] @deprecated(reason: \\"Please use the explicit _SET field\\") - partLocations_POP: Int - partLocations_PUSH: [CartesianPointInput!] - partLocations_SET: [CartesianPointInput!] + partLocations: ListCartesianPointInputMutations + partLocations_POP: Int @deprecated(reason: \\"Please use the generic mutation 'partLocations: { pop: ... } }' instead.\\") + partLocations_PUSH: [CartesianPointInput!] @deprecated(reason: \\"Please use the generic mutation 'partLocations: { push: ... } }' instead.\\") + partLocations_SET: [CartesianPointInput!] @deprecated(reason: \\"Please use the generic mutation 'partLocations: { set: ... } }' instead.\\") } input MachineWhere { AND: [MachineWhere!] NOT: MachineWhere OR: [MachineWhere!] - partLocations: [CartesianPointInput!] @deprecated(reason: \\"Please use the explicit _EQ version\\") - partLocations_EQ: [CartesianPointInput!] - partLocations_INCLUDES: CartesianPointInput + partLocations: CartesianPointListFilters + partLocations_EQ: [CartesianPointInput!] @deprecated(reason: \\"Please use the relevant generic filter partLocations: { eq: ... }\\") + partLocations_INCLUDES: CartesianPointInput @deprecated(reason: \\"Please use the relevant generic filter partLocations: { includes: ... }\\") } type MachinesConnection { @@ -598,7 +639,7 @@ describe("Point", () => { } type Query { - machines(limit: Int, offset: Int, options: MachineOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: MachineWhere): [Machine!]! + machines(limit: Int, offset: Int, where: MachineWhere): [Machine!]! machinesAggregate(where: MachineWhere): MachineAggregateSelection! machinesConnection(after: String, first: Int, where: MachineWhere): MachinesConnection! } diff --git a/packages/graphql/tests/schema/types/time.test.ts b/packages/graphql/tests/schema/types/time.test.ts index 423c6ade93..bc86494773 100644 --- a/packages/graphql/tests/schema/types/time.test.ts +++ b/packages/graphql/tests/schema/types/time.test.ts @@ -60,9 +60,18 @@ describe("Time", () => { relationshipsDeleted: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { @@ -72,7 +81,6 @@ describe("Time", () => { type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") time: TimeAggregateSelection! } @@ -86,15 +94,6 @@ describe("Time", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - \\"\\"\\" Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. \\"\\"\\" @@ -104,29 +103,29 @@ describe("Time", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID - time: Time @deprecated(reason: \\"Please use the explicit _SET field\\") - time_SET: Time + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") + time: TimeScalarMutations + time_SET: Time @deprecated(reason: \\"Please use the generic mutation 'time: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID - time: Time @deprecated(reason: \\"Please use the explicit _EQ version\\") - time_EQ: Time - time_GT: Time - time_GTE: Time - time_IN: [Time] - time_LT: Time - time_LTE: Time + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + time: TimeScalarFilters + time_EQ: Time @deprecated(reason: \\"Please use the relevant generic filter time: { eq: ... }\\") + time_GT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gt: ... }\\") + time_GTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { gte: ... }\\") + time_IN: [Time] @deprecated(reason: \\"Please use the relevant generic filter time: { in: ... }\\") + time_LT: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lt: ... }\\") + time_LTE: Time @deprecated(reason: \\"Please use the relevant generic filter time: { lte: ... }\\") } type MoviesConnection { @@ -150,7 +149,7 @@ describe("Time", () => { } type Query { - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! } @@ -171,6 +170,21 @@ describe("Time", () => { min: Time } + \\"\\"\\"Time filters\\"\\"\\" + input TimeScalarFilters { + eq: Time + gt: Time + gte: Time + in: [Time!] + lt: Time + lte: Time + } + + \\"\\"\\"Time mutations\\"\\"\\" + input TimeScalarMutations { + set: Time + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/schema/union-interface-relationship.test.ts b/packages/graphql/tests/schema/union-interface-relationship.test.ts index 3918fb36bf..0d31e963f5 100644 --- a/packages/graphql/tests/schema/union-interface-relationship.test.ts +++ b/packages/graphql/tests/schema/union-interface-relationship.test.ts @@ -29,12 +29,12 @@ describe("Union Interface Relationships", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN) reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN) - imdbId: Int @unique + imdbId: Int } type Actor @node { name: String! - id: Int @unique + id: Int movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) } @@ -53,8 +53,8 @@ describe("Union Interface Relationships", () => { type Person implements Reviewer @node { name: String! reputation: Int! - id: Int @unique - reviewerId: Int @unique + id: Int + reviewerId: Int movies: [Movie!]! @relationship(type: "REVIEWED", direction: OUT, properties: "Review") } @@ -93,26 +93,27 @@ describe("Union Interface Relationships", () => { AND: [ActedInAggregationWhereInput!] NOT: ActedInAggregationWhereInput OR: [ActedInAggregationWhereInput!] - screenTime_AVERAGE_EQUAL: Float - screenTime_AVERAGE_GT: Float - screenTime_AVERAGE_GTE: Float - screenTime_AVERAGE_LT: Float - screenTime_AVERAGE_LTE: Float - screenTime_MAX_EQUAL: Int - screenTime_MAX_GT: Int - screenTime_MAX_GTE: Int - screenTime_MAX_LT: Int - screenTime_MAX_LTE: Int - screenTime_MIN_EQUAL: Int - screenTime_MIN_GT: Int - screenTime_MIN_GTE: Int - screenTime_MIN_LT: Int - screenTime_MIN_LTE: Int - screenTime_SUM_EQUAL: Int - screenTime_SUM_GT: Int - screenTime_SUM_GTE: Int - screenTime_SUM_LT: Int - screenTime_SUM_LTE: Int + screenTime: IntScalarAggregationFilters + screenTime_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { eq: ... } } }' instead.\\") + screenTime_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gt: ... } } }' instead.\\") + screenTime_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { gte: ... } } }' instead.\\") + screenTime_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lt: ... } } }' instead.\\") + screenTime_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { average: { lte: ... } } }' instead.\\") + screenTime_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { eq: ... } } }' instead.\\") + screenTime_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gt: ... } } }' instead.\\") + screenTime_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { gte: ... } } }' instead.\\") + screenTime_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lt: ... } } }' instead.\\") + screenTime_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { max: { lte: ... } } }' instead.\\") + screenTime_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { eq: ... } } }' instead.\\") + screenTime_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gt: ... } } }' instead.\\") + screenTime_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { gte: ... } } }' instead.\\") + screenTime_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lt: ... } } }' instead.\\") + screenTime_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { min: { lte: ... } } }' instead.\\") + screenTime_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { eq: ... } } }' instead.\\") + screenTime_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gt: ... } } }' instead.\\") + screenTime_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { gte: ... } } }' instead.\\") + screenTime_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lt: ... } } }' instead.\\") + screenTime_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'screenTime: { sum: { lte: ... } } }' instead.\\") } input ActedInCreateInput { @@ -124,30 +125,30 @@ describe("Union Interface Relationships", () => { } input ActedInUpdateInput { - screenTime: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - screenTime_DECREMENT: Int - screenTime_INCREMENT: Int - screenTime_SET: Int + screenTime: IntScalarMutations + screenTime_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { decrement: ... } }' instead.\\") + screenTime_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'screenTime: { increment: ... } }' instead.\\") + screenTime_SET: Int @deprecated(reason: \\"Please use the generic mutation 'screenTime: { set: ... } }' instead.\\") } input ActedInWhere { AND: [ActedInWhere!] NOT: ActedInWhere OR: [ActedInWhere!] - screenTime: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - screenTime_EQ: Int - screenTime_GT: Int - screenTime_GTE: Int - screenTime_IN: [Int!] - screenTime_LT: Int - screenTime_LTE: Int + screenTime: IntScalarFilters + screenTime_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { eq: ... }\\") + screenTime_GT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gt: ... }\\") + screenTime_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { gte: ... }\\") + screenTime_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter screenTime: { in: ... }\\") + screenTime_LT: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lt: ... }\\") + screenTime_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter screenTime: { lte: ... }\\") } type Actor { id: Int - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): ActorMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): ActorMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [ActorMoviesConnectionSort!], where: ActorMoviesConnectionWhere): ActorMoviesConnection! name: String! } @@ -161,10 +162,6 @@ describe("Union Interface Relationships", () => { movies: [ActorMoviesConnectFieldInput!] } - input ActorConnectOrCreateWhere { - node: ActorUniqueWhere! - } - input ActorConnectWhere { node: ActorWhere! } @@ -207,7 +204,7 @@ describe("Union Interface Relationships", () => { AND: [ActorMoviesAggregateInput!] NOT: ActorMoviesAggregateInput OR: [ActorMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -220,29 +217,34 @@ describe("Union Interface Relationships", () => { input ActorMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } - input ActorMoviesConnectOrCreateFieldInput { - onCreate: ActorMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input ActorMoviesConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: MovieOnCreateInput! - } - type ActorMoviesConnection { edges: [ActorMoviesRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input ActorMoviesConnectionFilters { + \\"\\"\\" + Return Actors where all of the related ActorMoviesConnections match this filter + \\"\\"\\" + all: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where none of the related ActorMoviesConnections match this filter + \\"\\"\\" + none: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where one of the related ActorMoviesConnections match this filter + \\"\\"\\" + single: ActorMoviesConnectionWhere + \\"\\"\\" + Return Actors where some of the related ActorMoviesConnections match this filter + \\"\\"\\" + some: ActorMoviesConnectionWhere + } + input ActorMoviesConnectionSort { edge: ActedInSort node: MovieSort @@ -273,7 +275,6 @@ describe("Union Interface Relationships", () => { input ActorMoviesFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [ActorMoviesCreateFieldInput!] } @@ -281,41 +282,43 @@ describe("Union Interface Relationships", () => { AND: [ActorMoviesNodeAggregationWhereInput!] NOT: ActorMoviesNodeAggregationWhereInput OR: [ActorMoviesNodeAggregationWhereInput!] - imdbId_AVERAGE_EQUAL: Float - imdbId_AVERAGE_GT: Float - imdbId_AVERAGE_GTE: Float - imdbId_AVERAGE_LT: Float - imdbId_AVERAGE_LTE: Float - imdbId_MAX_EQUAL: Int - imdbId_MAX_GT: Int - imdbId_MAX_GTE: Int - imdbId_MAX_LT: Int - imdbId_MAX_LTE: Int - imdbId_MIN_EQUAL: Int - imdbId_MIN_GT: Int - imdbId_MIN_GTE: Int - imdbId_MIN_LT: Int - imdbId_MIN_LTE: Int - imdbId_SUM_EQUAL: Int - imdbId_SUM_GT: Int - imdbId_SUM_GTE: Int - imdbId_SUM_LT: Int - imdbId_SUM_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + imdbId: IntScalarAggregationFilters + imdbId_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { eq: ... } } }' instead.\\") + imdbId_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { gt: ... } } }' instead.\\") + imdbId_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { gte: ... } } }' instead.\\") + imdbId_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { lt: ... } } }' instead.\\") + imdbId_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { lte: ... } } }' instead.\\") + imdbId_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { eq: ... } } }' instead.\\") + imdbId_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { gt: ... } } }' instead.\\") + imdbId_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { gte: ... } } }' instead.\\") + imdbId_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { lt: ... } } }' instead.\\") + imdbId_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { lte: ... } } }' instead.\\") + imdbId_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { eq: ... } } }' instead.\\") + imdbId_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { gt: ... } } }' instead.\\") + imdbId_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { gte: ... } } }' instead.\\") + imdbId_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { lt: ... } } }' instead.\\") + imdbId_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { lte: ... } } }' instead.\\") + imdbId_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { eq: ... } } }' instead.\\") + imdbId_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { gt: ... } } }' instead.\\") + imdbId_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { gte: ... } } }' instead.\\") + imdbId_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { lt: ... } } }' instead.\\") + imdbId_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type ActorMoviesRelationship { @@ -331,7 +334,6 @@ describe("Union Interface Relationships", () => { input ActorMoviesUpdateFieldInput { connect: [ActorMoviesConnectFieldInput!] - connectOrCreate: [ActorMoviesConnectOrCreateFieldInput!] create: [ActorMoviesCreateFieldInput!] delete: [ActorMoviesDeleteFieldInput!] disconnect: [ActorMoviesDisconnectFieldInput!] @@ -339,18 +341,15 @@ describe("Union Interface Relationships", () => { where: ActorMoviesConnectionWhere } - input ActorOnCreateInput { - id: Int - name: String! - } - - input ActorOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ActorSort objects to sort Actors by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ActorSort!] + input ActorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Actors match this filter\\"\\"\\" + all: ActorWhere + \\"\\"\\"Filter type where none of the related Actors match this filter\\"\\"\\" + none: ActorWhere + \\"\\"\\"Filter type where one of the related Actors match this filter\\"\\"\\" + single: ActorWhere + \\"\\"\\"Filter type where some of the related Actors match this filter\\"\\"\\" + some: ActorWhere } \\"\\"\\" @@ -361,63 +360,60 @@ describe("Union Interface Relationships", () => { name: SortDirection } - input ActorUniqueWhere { - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - } - input ActorUpdateInput { - id: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - id_DECREMENT: Int - id_INCREMENT: Int - id_SET: Int + id: IntScalarMutations + id_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'id: { decrement: ... } }' instead.\\") + id_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'id: { increment: ... } }' instead.\\") + id_SET: Int @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") movies: [ActorMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") } input ActorWhere { AND: [ActorWhere!] NOT: ActorWhere OR: [ActorWhere!] - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - id_GT: Int - id_GTE: Int - id_IN: [Int] - id_LT: Int - id_LTE: Int + id: IntScalarFilters + id_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_GT: Int @deprecated(reason: \\"Please use the relevant generic filter id: { gt: ... }\\") + id_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter id: { gte: ... }\\") + id_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_LT: Int @deprecated(reason: \\"Please use the relevant generic filter id: { lt: ... }\\") + id_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter id: { lte: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: ActorMoviesAggregateInput + moviesConnection: ActorMoviesConnectionFilters \\"\\"\\" Return Actors where all of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: ActorMoviesConnectionWhere + moviesConnection_ALL: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where none of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: ActorMoviesConnectionWhere + moviesConnection_NONE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where one of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: ActorMoviesConnectionWhere + moviesConnection_SINGLE: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Actors where some of the related ActorMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: ActorMoviesConnectionWhere + moviesConnection_SOME: ActorMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Actors where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return Actors where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return Actors where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return Actors where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") } type ActorsConnection { @@ -479,32 +475,53 @@ describe("Union Interface Relationships", () => { } input DirectedUpdateInput { - year: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - year_DECREMENT: Int - year_INCREMENT: Int - year_SET: Int + year: IntScalarMutations + year_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { decrement: ... } }' instead.\\") + year_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'year: { increment: ... } }' instead.\\") + year_SET: Int @deprecated(reason: \\"Please use the generic mutation 'year: { set: ... } }' instead.\\") } input DirectedWhere { AND: [DirectedWhere!] NOT: DirectedWhere OR: [DirectedWhere!] - year: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - year_EQ: Int - year_GT: Int - year_GTE: Int - year_IN: [Int!] - year_LT: Int - year_LTE: Int + year: IntScalarFilters + year_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter year: { eq: ... }\\") + year_GT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gt: ... }\\") + year_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { gte: ... }\\") + year_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter year: { in: ... }\\") + year_LT: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lt: ... }\\") + year_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter year: { lte: ... }\\") } union Director = Actor | Person + input DirectorRelationshipFilters { + \\"\\"\\"Filter type where all of the related Directors match this filter\\"\\"\\" + all: DirectorWhere + \\"\\"\\"Filter type where none of the related Directors match this filter\\"\\"\\" + none: DirectorWhere + \\"\\"\\"Filter type where one of the related Directors match this filter\\"\\"\\" + single: DirectorWhere + \\"\\"\\"Filter type where some of the related Directors match this filter\\"\\"\\" + some: DirectorWhere + } + input DirectorWhere { Actor: ActorWhere Person: PersonWhere } + \\"\\"\\"Float filters\\"\\"\\" + input FloatScalarFilters { + eq: Float + gt: Float + gte: Float + in: [Float!] + lt: Float + lte: Float + } + type Influencer implements Reviewer { reputation: Int! reviewerId: Int @@ -529,15 +546,6 @@ describe("Union Interface Relationships", () => { node: Influencer! } - input InfluencerOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more InfluencerSort objects to sort Influencers by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [InfluencerSort!] - } - \\"\\"\\" Fields to sort Influencers by. The order in which sorts are applied is not guaranteed when specifying many fields in one InfluencerSort object. \\"\\"\\" @@ -548,42 +556,42 @@ describe("Union Interface Relationships", () => { } input InfluencerUpdateInput { - reputation: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reputation_DECREMENT: Int - reputation_INCREMENT: Int - reputation_SET: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reviewerId_DECREMENT: Int - reviewerId_INCREMENT: Int - reviewerId_SET: Int - url: String @deprecated(reason: \\"Please use the explicit _SET field\\") - url_SET: String + reputation: IntScalarMutations + reputation_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { decrement: ... } }' instead.\\") + reputation_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { increment: ... } }' instead.\\") + reputation_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reputation: { set: ... } }' instead.\\") + reviewerId: IntScalarMutations + reviewerId_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { decrement: ... } }' instead.\\") + reviewerId_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { increment: ... } }' instead.\\") + reviewerId_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reviewerId: { set: ... } }' instead.\\") + url: StringScalarMutations + url_SET: String @deprecated(reason: \\"Please use the generic mutation 'url: { set: ... } }' instead.\\") } input InfluencerWhere { AND: [InfluencerWhere!] NOT: InfluencerWhere OR: [InfluencerWhere!] - reputation: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reputation_EQ: Int - reputation_GT: Int - reputation_GTE: Int - reputation_IN: [Int!] - reputation_LT: Int - reputation_LTE: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reviewerId_EQ: Int - reviewerId_GT: Int - reviewerId_GTE: Int - reviewerId_IN: [Int] - reviewerId_LT: Int - reviewerId_LTE: Int - url: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - url_CONTAINS: String - url_ENDS_WITH: String - url_EQ: String - url_IN: [String!] - url_STARTS_WITH: String + reputation: IntScalarFilters + reputation_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { eq: ... }\\") + reputation_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gt: ... }\\") + reputation_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gte: ... }\\") + reputation_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter reputation: { in: ... }\\") + reputation_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lt: ... }\\") + reputation_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lte: ... }\\") + reviewerId: IntScalarFilters + reviewerId_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { eq: ... }\\") + reviewerId_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gt: ... }\\") + reviewerId_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gte: ... }\\") + reviewerId_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { in: ... }\\") + reviewerId_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lt: ... }\\") + reviewerId_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lte: ... }\\") + url: StringScalarFilters + url_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter url: { contains: ... }\\") + url_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter url: { endsWith: ... }\\") + url_EQ: String @deprecated(reason: \\"Please use the relevant generic filter url: { eq: ... }\\") + url_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter url: { in: ... }\\") + url_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter url: { startsWith: ... }\\") } type InfluencersConnection { @@ -599,16 +607,41 @@ describe("Union Interface Relationships", () => { sum: Int } + \\"\\"\\"Filters for an aggregation of an int field\\"\\"\\" + input IntScalarAggregationFilters { + average: FloatScalarFilters + max: IntScalarFilters + min: IntScalarFilters + sum: IntScalarFilters + } + + \\"\\"\\"Int filters\\"\\"\\" + input IntScalarFilters { + eq: Int + gt: Int + gte: Int + in: [Int!] + lt: Int + lte: Int + } + + \\"\\"\\"Int mutations\\"\\"\\" + input IntScalarMutations { + add: Int + set: Int + subtract: Int + } + type Movie { - actors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! - actorsAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ActorWhere): MovieActorActorsAggregationSelection - actorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! - directors(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: DirectorWhere): [Director!]! - directorsConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieDirectorsConnectionSort!], where: MovieDirectorsConnectionWhere): MovieDirectorsConnection! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! + actorsAggregate(where: ActorWhere): MovieActorActorsAggregationSelection + actorsConnection(after: String, first: Int, sort: [MovieActorsConnectionSort!], where: MovieActorsConnectionWhere): MovieActorsConnection! + directors(limit: Int, offset: Int, where: DirectorWhere): [Director!]! + directorsConnection(after: String, first: Int, sort: [MovieDirectorsConnectionSort!], where: MovieDirectorsConnectionWhere): MovieDirectorsConnection! imdbId: Int - reviewers(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: ReviewerOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ReviewerSort!], where: ReviewerWhere): [Reviewer!]! - reviewersAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: ReviewerWhere): MovieReviewerReviewersAggregationSelection - reviewersConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [MovieReviewersConnectionSort!], where: MovieReviewersConnectionWhere): MovieReviewersConnection! + reviewers(limit: Int, offset: Int, sort: [ReviewerSort!], where: ReviewerWhere): [Reviewer!]! + reviewersAggregate(where: ReviewerWhere): MovieReviewerReviewersAggregationSelection + reviewersConnection(after: String, first: Int, sort: [MovieReviewersConnectionSort!], where: MovieReviewersConnectionWhere): MovieReviewersConnection! title: String! } @@ -631,7 +664,7 @@ describe("Union Interface Relationships", () => { AND: [MovieActorsAggregateInput!] NOT: MovieActorsAggregateInput OR: [MovieActorsAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -644,29 +677,34 @@ describe("Union Interface Relationships", () => { input MovieActorsConnectFieldInput { connect: [ActorConnectInput!] edge: ActedInCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: ActorConnectWhere } - input MovieActorsConnectOrCreateFieldInput { - onCreate: MovieActorsConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieActorsConnectOrCreateFieldInputOnCreate { - edge: ActedInCreateInput! - node: ActorOnCreateInput! - } - type MovieActorsConnection { edges: [MovieActorsRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input MovieActorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieActorsConnections match this filter + \\"\\"\\" + all: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieActorsConnections match this filter + \\"\\"\\" + none: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieActorsConnections match this filter + \\"\\"\\" + single: MovieActorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieActorsConnections match this filter + \\"\\"\\" + some: MovieActorsConnectionWhere + } + input MovieActorsConnectionSort { edge: ActedInSort node: ActorSort @@ -697,7 +735,6 @@ describe("Union Interface Relationships", () => { input MovieActorsFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieActorsCreateFieldInput!] } @@ -705,41 +742,43 @@ describe("Union Interface Relationships", () => { AND: [MovieActorsNodeAggregationWhereInput!] NOT: MovieActorsNodeAggregationWhereInput OR: [MovieActorsNodeAggregationWhereInput!] - id_AVERAGE_EQUAL: Float - id_AVERAGE_GT: Float - id_AVERAGE_GTE: Float - id_AVERAGE_LT: Float - id_AVERAGE_LTE: Float - id_MAX_EQUAL: Int - id_MAX_GT: Int - id_MAX_GTE: Int - id_MAX_LT: Int - id_MAX_LTE: Int - id_MIN_EQUAL: Int - id_MIN_GT: Int - id_MIN_GTE: Int - id_MIN_LT: Int - id_MIN_LTE: Int - id_SUM_EQUAL: Int - id_SUM_GT: Int - id_SUM_GTE: Int - id_SUM_LT: Int - id_SUM_LTE: Int - name_AVERAGE_LENGTH_EQUAL: Float - name_AVERAGE_LENGTH_GT: Float - name_AVERAGE_LENGTH_GTE: Float - name_AVERAGE_LENGTH_LT: Float - name_AVERAGE_LENGTH_LTE: Float - name_LONGEST_LENGTH_EQUAL: Int - name_LONGEST_LENGTH_GT: Int - name_LONGEST_LENGTH_GTE: Int - name_LONGEST_LENGTH_LT: Int - name_LONGEST_LENGTH_LTE: Int - name_SHORTEST_LENGTH_EQUAL: Int - name_SHORTEST_LENGTH_GT: Int - name_SHORTEST_LENGTH_GTE: Int - name_SHORTEST_LENGTH_LT: Int - name_SHORTEST_LENGTH_LTE: Int + id: IntScalarAggregationFilters + id_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { average: { eq: ... } } }' instead.\\") + id_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { average: { gt: ... } } }' instead.\\") + id_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { average: { gte: ... } } }' instead.\\") + id_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { average: { lt: ... } } }' instead.\\") + id_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'id: { average: { lte: ... } } }' instead.\\") + id_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { max: { eq: ... } } }' instead.\\") + id_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { max: { gt: ... } } }' instead.\\") + id_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { max: { gte: ... } } }' instead.\\") + id_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { max: { lt: ... } } }' instead.\\") + id_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { max: { lte: ... } } }' instead.\\") + id_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { min: { eq: ... } } }' instead.\\") + id_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { min: { gt: ... } } }' instead.\\") + id_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { min: { gte: ... } } }' instead.\\") + id_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { min: { lt: ... } } }' instead.\\") + id_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { min: { lte: ... } } }' instead.\\") + id_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { sum: { eq: ... } } }' instead.\\") + id_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { sum: { gt: ... } } }' instead.\\") + id_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { sum: { gte: ... } } }' instead.\\") + id_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { sum: { lt: ... } } }' instead.\\") + id_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'id: { sum: { lte: ... } } }' instead.\\") + name: StringScalarAggregationFilters + name_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { eq: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { gte: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lt: ... } } }' instead.\\") + name_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'name: { averageLength: { lte: ... } } }' instead.\\") + name_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { eq: ... } } }' instead.\\") + name_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gt: ... } } }' instead.\\") + name_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { gte: ... } } }' instead.\\") + name_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lt: ... } } }' instead.\\") + name_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { longestLength: { lte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { eq: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { gte: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lt: ... } } }' instead.\\") + name_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'name: { shortestLength: { lte: ... } } }' instead.\\") } type MovieActorsRelationship { @@ -755,7 +794,6 @@ describe("Union Interface Relationships", () => { input MovieActorsUpdateFieldInput { connect: [MovieActorsConnectFieldInput!] - connectOrCreate: [MovieActorsConnectOrCreateFieldInput!] create: [MovieActorsCreateFieldInput!] delete: [MovieActorsDeleteFieldInput!] disconnect: [MovieActorsDisconnectFieldInput!] @@ -775,10 +813,6 @@ describe("Union Interface Relationships", () => { reviewers: [MovieReviewersConnectFieldInput!] } - input MovieConnectOrCreateWhere { - node: MovieUniqueWhere! - } - input MovieConnectWhere { node: MovieWhere! } @@ -803,16 +837,6 @@ describe("Union Interface Relationships", () => { where: ActorConnectWhere } - input MovieDirectorsActorConnectOrCreateFieldInput { - onCreate: MovieDirectorsActorConnectOrCreateFieldInputOnCreate! - where: ActorConnectOrCreateWhere! - } - - input MovieDirectorsActorConnectOrCreateFieldInputOnCreate { - edge: DirectedCreateInput! - node: ActorOnCreateInput! - } - input MovieDirectorsActorConnectionWhere { AND: [MovieDirectorsActorConnectionWhere!] NOT: MovieDirectorsActorConnectionWhere @@ -838,7 +862,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsActorFieldInput { connect: [MovieDirectorsActorConnectFieldInput!] - connectOrCreate: [MovieDirectorsActorConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieDirectorsActorCreateFieldInput!] } @@ -849,7 +872,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsActorUpdateFieldInput { connect: [MovieDirectorsActorConnectFieldInput!] - connectOrCreate: [MovieDirectorsActorConnectOrCreateFieldInput!] create: [MovieDirectorsActorCreateFieldInput!] delete: [MovieDirectorsActorDeleteFieldInput!] disconnect: [MovieDirectorsActorDisconnectFieldInput!] @@ -868,6 +890,25 @@ describe("Union Interface Relationships", () => { totalCount: Int! } + input MovieDirectorsConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieDirectorsConnections match this filter + \\"\\"\\" + all: MovieDirectorsConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieDirectorsConnections match this filter + \\"\\"\\" + none: MovieDirectorsConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieDirectorsConnections match this filter + \\"\\"\\" + single: MovieDirectorsConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieDirectorsConnections match this filter + \\"\\"\\" + some: MovieDirectorsConnectionWhere + } + input MovieDirectorsConnectionSort { edge: DirectedSort } @@ -898,16 +939,6 @@ describe("Union Interface Relationships", () => { where: PersonConnectWhere } - input MovieDirectorsPersonConnectOrCreateFieldInput { - onCreate: MovieDirectorsPersonConnectOrCreateFieldInputOnCreate! - where: PersonConnectOrCreateWhere! - } - - input MovieDirectorsPersonConnectOrCreateFieldInputOnCreate { - edge: DirectedCreateInput! - node: PersonOnCreateInput! - } - input MovieDirectorsPersonConnectionWhere { AND: [MovieDirectorsPersonConnectionWhere!] NOT: MovieDirectorsPersonConnectionWhere @@ -933,7 +964,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsPersonFieldInput { connect: [MovieDirectorsPersonConnectFieldInput!] - connectOrCreate: [MovieDirectorsPersonConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [MovieDirectorsPersonCreateFieldInput!] } @@ -944,7 +974,6 @@ describe("Union Interface Relationships", () => { input MovieDirectorsPersonUpdateFieldInput { connect: [MovieDirectorsPersonConnectFieldInput!] - connectOrCreate: [MovieDirectorsPersonConnectOrCreateFieldInput!] create: [MovieDirectorsPersonCreateFieldInput!] delete: [MovieDirectorsPersonDeleteFieldInput!] disconnect: [MovieDirectorsPersonDisconnectFieldInput!] @@ -974,18 +1003,15 @@ describe("Union Interface Relationships", () => { node: Movie! } - input MovieOnCreateInput { - imdbId: Int - title: String! - } - - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] + input MovieRelationshipFilters { + \\"\\"\\"Filter type where all of the related Movies match this filter\\"\\"\\" + all: MovieWhere + \\"\\"\\"Filter type where none of the related Movies match this filter\\"\\"\\" + none: MovieWhere + \\"\\"\\"Filter type where one of the related Movies match this filter\\"\\"\\" + single: MovieWhere + \\"\\"\\"Filter type where some of the related Movies match this filter\\"\\"\\" + some: MovieWhere } type MovieReviewerReviewersAggregationSelection { @@ -1007,7 +1033,7 @@ describe("Union Interface Relationships", () => { AND: [MovieReviewersAggregateInput!] NOT: MovieReviewersAggregateInput OR: [MovieReviewersAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1028,6 +1054,25 @@ describe("Union Interface Relationships", () => { totalCount: Int! } + input MovieReviewersConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieReviewersConnections match this filter + \\"\\"\\" + all: MovieReviewersConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieReviewersConnections match this filter + \\"\\"\\" + none: MovieReviewersConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieReviewersConnections match this filter + \\"\\"\\" + single: MovieReviewersConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieReviewersConnections match this filter + \\"\\"\\" + some: MovieReviewersConnectionWhere + } + input MovieReviewersConnectionSort { edge: ReviewSort node: ReviewerSort @@ -1063,46 +1108,48 @@ describe("Union Interface Relationships", () => { AND: [MovieReviewersNodeAggregationWhereInput!] NOT: MovieReviewersNodeAggregationWhereInput OR: [MovieReviewersNodeAggregationWhereInput!] - reputation_AVERAGE_EQUAL: Float - reputation_AVERAGE_GT: Float - reputation_AVERAGE_GTE: Float - reputation_AVERAGE_LT: Float - reputation_AVERAGE_LTE: Float - reputation_MAX_EQUAL: Int - reputation_MAX_GT: Int - reputation_MAX_GTE: Int - reputation_MAX_LT: Int - reputation_MAX_LTE: Int - reputation_MIN_EQUAL: Int - reputation_MIN_GT: Int - reputation_MIN_GTE: Int - reputation_MIN_LT: Int - reputation_MIN_LTE: Int - reputation_SUM_EQUAL: Int - reputation_SUM_GT: Int - reputation_SUM_GTE: Int - reputation_SUM_LT: Int - reputation_SUM_LTE: Int - reviewerId_AVERAGE_EQUAL: Float - reviewerId_AVERAGE_GT: Float - reviewerId_AVERAGE_GTE: Float - reviewerId_AVERAGE_LT: Float - reviewerId_AVERAGE_LTE: Float - reviewerId_MAX_EQUAL: Int - reviewerId_MAX_GT: Int - reviewerId_MAX_GTE: Int - reviewerId_MAX_LT: Int - reviewerId_MAX_LTE: Int - reviewerId_MIN_EQUAL: Int - reviewerId_MIN_GT: Int - reviewerId_MIN_GTE: Int - reviewerId_MIN_LT: Int - reviewerId_MIN_LTE: Int - reviewerId_SUM_EQUAL: Int - reviewerId_SUM_GT: Int - reviewerId_SUM_GTE: Int - reviewerId_SUM_LT: Int - reviewerId_SUM_LTE: Int + reputation: IntScalarAggregationFilters + reputation_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { average: { eq: ... } } }' instead.\\") + reputation_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { average: { gt: ... } } }' instead.\\") + reputation_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { average: { gte: ... } } }' instead.\\") + reputation_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { average: { lt: ... } } }' instead.\\") + reputation_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { average: { lte: ... } } }' instead.\\") + reputation_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { max: { eq: ... } } }' instead.\\") + reputation_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { max: { gt: ... } } }' instead.\\") + reputation_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { max: { gte: ... } } }' instead.\\") + reputation_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { max: { lt: ... } } }' instead.\\") + reputation_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { max: { lte: ... } } }' instead.\\") + reputation_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { min: { eq: ... } } }' instead.\\") + reputation_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { min: { gt: ... } } }' instead.\\") + reputation_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { min: { gte: ... } } }' instead.\\") + reputation_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { min: { lt: ... } } }' instead.\\") + reputation_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { min: { lte: ... } } }' instead.\\") + reputation_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { sum: { eq: ... } } }' instead.\\") + reputation_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { sum: { gt: ... } } }' instead.\\") + reputation_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { sum: { gte: ... } } }' instead.\\") + reputation_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { sum: { lt: ... } } }' instead.\\") + reputation_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reputation: { sum: { lte: ... } } }' instead.\\") + reviewerId: IntScalarAggregationFilters + reviewerId_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { average: { eq: ... } } }' instead.\\") + reviewerId_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { average: { gt: ... } } }' instead.\\") + reviewerId_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { average: { gte: ... } } }' instead.\\") + reviewerId_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { average: { lt: ... } } }' instead.\\") + reviewerId_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { average: { lte: ... } } }' instead.\\") + reviewerId_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { max: { eq: ... } } }' instead.\\") + reviewerId_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { max: { gt: ... } } }' instead.\\") + reviewerId_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { max: { gte: ... } } }' instead.\\") + reviewerId_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { max: { lt: ... } } }' instead.\\") + reviewerId_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { max: { lte: ... } } }' instead.\\") + reviewerId_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { min: { eq: ... } } }' instead.\\") + reviewerId_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { min: { gt: ... } } }' instead.\\") + reviewerId_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { min: { gte: ... } } }' instead.\\") + reviewerId_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { min: { lt: ... } } }' instead.\\") + reviewerId_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { min: { lte: ... } } }' instead.\\") + reviewerId_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { sum: { eq: ... } } }' instead.\\") + reviewerId_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { sum: { gt: ... } } }' instead.\\") + reviewerId_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { sum: { gte: ... } } }' instead.\\") + reviewerId_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { sum: { lt: ... } } }' instead.\\") + reviewerId_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'reviewerId: { sum: { lte: ... } } }' instead.\\") } type MovieReviewersRelationship { @@ -1133,114 +1180,115 @@ describe("Union Interface Relationships", () => { title: SortDirection } - input MovieUniqueWhere { - imdbId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbId_EQ: Int - } - input MovieUpdateInput { actors: [MovieActorsUpdateFieldInput!] directors: MovieDirectorsUpdateInput - imdbId: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - imdbId_DECREMENT: Int - imdbId_INCREMENT: Int - imdbId_SET: Int + imdbId: IntScalarMutations + imdbId_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'imdbId: { decrement: ... } }' instead.\\") + imdbId_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'imdbId: { increment: ... } }' instead.\\") + imdbId_SET: Int @deprecated(reason: \\"Please use the generic mutation 'imdbId: { set: ... } }' instead.\\") reviewers: [MovieReviewersUpdateFieldInput!] - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") } input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] + actors: ActorRelationshipFilters actorsAggregate: MovieActorsAggregateInput + actorsConnection: MovieActorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_ALL: MovieActorsConnectionWhere + actorsConnection_ALL: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_NONE: MovieActorsConnectionWhere + actorsConnection_NONE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SINGLE: MovieActorsConnectionWhere + actorsConnection_SINGLE: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieActorsConnections match this filter \\"\\"\\" - actorsConnection_SOME: MovieActorsConnectionWhere + actorsConnection_SOME: MovieActorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'actorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Actors match this filter\\"\\"\\" - actors_ALL: ActorWhere + actors_ALL: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Actors match this filter\\"\\"\\" - actors_NONE: ActorWhere + actors_NONE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Actors match this filter\\"\\"\\" - actors_SINGLE: ActorWhere + actors_SINGLE: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Actors match this filter\\"\\"\\" - actors_SOME: ActorWhere + actors_SOME: ActorWhere @deprecated(reason: \\"Please use the relevant generic filter 'actors: { some: ... }' instead.\\") + directors: DirectorRelationshipFilters + directorsConnection: MovieDirectorsConnectionFilters \\"\\"\\" Return Movies where all of the related MovieDirectorsConnections match this filter \\"\\"\\" - directorsConnection_ALL: MovieDirectorsConnectionWhere + directorsConnection_ALL: MovieDirectorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorsConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieDirectorsConnections match this filter \\"\\"\\" - directorsConnection_NONE: MovieDirectorsConnectionWhere + directorsConnection_NONE: MovieDirectorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorsConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieDirectorsConnections match this filter \\"\\"\\" - directorsConnection_SINGLE: MovieDirectorsConnectionWhere + directorsConnection_SINGLE: MovieDirectorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorsConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieDirectorsConnections match this filter \\"\\"\\" - directorsConnection_SOME: MovieDirectorsConnectionWhere + directorsConnection_SOME: MovieDirectorsConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'directorsConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Directors match this filter\\"\\"\\" - directors_ALL: DirectorWhere + directors_ALL: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directors: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Directors match this filter\\"\\"\\" - directors_NONE: DirectorWhere + directors_NONE: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directors: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Directors match this filter\\"\\"\\" - directors_SINGLE: DirectorWhere + directors_SINGLE: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directors: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Directors match this filter\\"\\"\\" - directors_SOME: DirectorWhere - imdbId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - imdbId_EQ: Int - imdbId_GT: Int - imdbId_GTE: Int - imdbId_IN: [Int] - imdbId_LT: Int - imdbId_LTE: Int + directors_SOME: DirectorWhere @deprecated(reason: \\"Please use the relevant generic filter 'directors: { some: ... }' instead.\\") + imdbId: IntScalarFilters + imdbId_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter imdbId: { eq: ... }\\") + imdbId_GT: Int @deprecated(reason: \\"Please use the relevant generic filter imdbId: { gt: ... }\\") + imdbId_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter imdbId: { gte: ... }\\") + imdbId_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter imdbId: { in: ... }\\") + imdbId_LT: Int @deprecated(reason: \\"Please use the relevant generic filter imdbId: { lt: ... }\\") + imdbId_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter imdbId: { lte: ... }\\") + reviewers: ReviewerRelationshipFilters reviewersAggregate: MovieReviewersAggregateInput + reviewersConnection: MovieReviewersConnectionFilters \\"\\"\\" Return Movies where all of the related MovieReviewersConnections match this filter \\"\\"\\" - reviewersConnection_ALL: MovieReviewersConnectionWhere + reviewersConnection_ALL: MovieReviewersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewersConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieReviewersConnections match this filter \\"\\"\\" - reviewersConnection_NONE: MovieReviewersConnectionWhere + reviewersConnection_NONE: MovieReviewersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewersConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieReviewersConnections match this filter \\"\\"\\" - reviewersConnection_SINGLE: MovieReviewersConnectionWhere + reviewersConnection_SINGLE: MovieReviewersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewersConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieReviewersConnections match this filter \\"\\"\\" - reviewersConnection_SOME: MovieReviewersConnectionWhere + reviewersConnection_SOME: MovieReviewersConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewersConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Reviewers match this filter\\"\\"\\" - reviewers_ALL: ReviewerWhere + reviewers_ALL: ReviewerWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewers: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Reviewers match this filter\\"\\"\\" - reviewers_NONE: ReviewerWhere + reviewers_NONE: ReviewerWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewers: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Reviewers match this filter\\"\\"\\" - reviewers_SINGLE: ReviewerWhere + reviewers_SINGLE: ReviewerWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewers: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Reviewers match this filter\\"\\"\\" - reviewers_SOME: ReviewerWhere - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String!] - title_STARTS_WITH: String + reviewers_SOME: ReviewerWhere @deprecated(reason: \\"Please use the relevant generic filter 'reviewers: { some: ... }' instead.\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -1280,9 +1328,9 @@ describe("Union Interface Relationships", () => { type Person implements Reviewer { id: Int - movies(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! - moviesAggregate(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), where: MovieWhere): PersonMovieMoviesAggregationSelection - moviesConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! + moviesAggregate(where: MovieWhere): PersonMovieMoviesAggregationSelection + moviesConnection(after: String, first: Int, sort: [PersonMoviesConnectionSort!], where: PersonMoviesConnectionWhere): PersonMoviesConnection! name: String! reputation: Int! reviewerId: Int @@ -1300,10 +1348,6 @@ describe("Union Interface Relationships", () => { movies: [PersonMoviesConnectFieldInput!] } - input PersonConnectOrCreateWhere { - node: PersonUniqueWhere! - } - input PersonConnectWhere { node: PersonWhere! } @@ -1348,7 +1392,7 @@ describe("Union Interface Relationships", () => { AND: [PersonMoviesAggregateInput!] NOT: PersonMoviesAggregateInput OR: [PersonMoviesAggregateInput!] - count: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") + count: IntScalarFilters count_EQ: Int count_GT: Int count_GTE: Int @@ -1361,29 +1405,34 @@ describe("Union Interface Relationships", () => { input PersonMoviesConnectFieldInput { connect: [MovieConnectInput!] edge: ReviewCreateInput! - \\"\\"\\" - Whether or not to overwrite any matching relationship with the new properties. - \\"\\"\\" - overwrite: Boolean! = true @deprecated(reason: \\"The overwrite argument is deprecated and will be removed\\") where: MovieConnectWhere } - input PersonMoviesConnectOrCreateFieldInput { - onCreate: PersonMoviesConnectOrCreateFieldInputOnCreate! - where: MovieConnectOrCreateWhere! - } - - input PersonMoviesConnectOrCreateFieldInputOnCreate { - edge: ReviewCreateInput! - node: MovieOnCreateInput! - } - type PersonMoviesConnection { edges: [PersonMoviesRelationship!]! pageInfo: PageInfo! totalCount: Int! } + input PersonMoviesConnectionFilters { + \\"\\"\\" + Return People where all of the related PersonMoviesConnections match this filter + \\"\\"\\" + all: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where none of the related PersonMoviesConnections match this filter + \\"\\"\\" + none: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where one of the related PersonMoviesConnections match this filter + \\"\\"\\" + single: PersonMoviesConnectionWhere + \\"\\"\\" + Return People where some of the related PersonMoviesConnections match this filter + \\"\\"\\" + some: PersonMoviesConnectionWhere + } + input PersonMoviesConnectionSort { edge: ReviewSort node: MovieSort @@ -1414,7 +1463,6 @@ describe("Union Interface Relationships", () => { input PersonMoviesFieldInput { connect: [PersonMoviesConnectFieldInput!] - connectOrCreate: [PersonMoviesConnectOrCreateFieldInput!] @deprecated(reason: \\"The connectOrCreate operation is deprecated and will be removed\\") create: [PersonMoviesCreateFieldInput!] } @@ -1422,41 +1470,43 @@ describe("Union Interface Relationships", () => { AND: [PersonMoviesNodeAggregationWhereInput!] NOT: PersonMoviesNodeAggregationWhereInput OR: [PersonMoviesNodeAggregationWhereInput!] - imdbId_AVERAGE_EQUAL: Float - imdbId_AVERAGE_GT: Float - imdbId_AVERAGE_GTE: Float - imdbId_AVERAGE_LT: Float - imdbId_AVERAGE_LTE: Float - imdbId_MAX_EQUAL: Int - imdbId_MAX_GT: Int - imdbId_MAX_GTE: Int - imdbId_MAX_LT: Int - imdbId_MAX_LTE: Int - imdbId_MIN_EQUAL: Int - imdbId_MIN_GT: Int - imdbId_MIN_GTE: Int - imdbId_MIN_LT: Int - imdbId_MIN_LTE: Int - imdbId_SUM_EQUAL: Int - imdbId_SUM_GT: Int - imdbId_SUM_GTE: Int - imdbId_SUM_LT: Int - imdbId_SUM_LTE: Int - title_AVERAGE_LENGTH_EQUAL: Float - title_AVERAGE_LENGTH_GT: Float - title_AVERAGE_LENGTH_GTE: Float - title_AVERAGE_LENGTH_LT: Float - title_AVERAGE_LENGTH_LTE: Float - title_LONGEST_LENGTH_EQUAL: Int - title_LONGEST_LENGTH_GT: Int - title_LONGEST_LENGTH_GTE: Int - title_LONGEST_LENGTH_LT: Int - title_LONGEST_LENGTH_LTE: Int - title_SHORTEST_LENGTH_EQUAL: Int - title_SHORTEST_LENGTH_GT: Int - title_SHORTEST_LENGTH_GTE: Int - title_SHORTEST_LENGTH_LT: Int - title_SHORTEST_LENGTH_LTE: Int + imdbId: IntScalarAggregationFilters + imdbId_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { eq: ... } } }' instead.\\") + imdbId_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { gt: ... } } }' instead.\\") + imdbId_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { gte: ... } } }' instead.\\") + imdbId_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { lt: ... } } }' instead.\\") + imdbId_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { average: { lte: ... } } }' instead.\\") + imdbId_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { eq: ... } } }' instead.\\") + imdbId_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { gt: ... } } }' instead.\\") + imdbId_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { gte: ... } } }' instead.\\") + imdbId_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { lt: ... } } }' instead.\\") + imdbId_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { max: { lte: ... } } }' instead.\\") + imdbId_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { eq: ... } } }' instead.\\") + imdbId_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { gt: ... } } }' instead.\\") + imdbId_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { gte: ... } } }' instead.\\") + imdbId_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { lt: ... } } }' instead.\\") + imdbId_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { min: { lte: ... } } }' instead.\\") + imdbId_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { eq: ... } } }' instead.\\") + imdbId_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { gt: ... } } }' instead.\\") + imdbId_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { gte: ... } } }' instead.\\") + imdbId_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { lt: ... } } }' instead.\\") + imdbId_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'imdbId: { sum: { lte: ... } } }' instead.\\") + title: StringScalarAggregationFilters + title_AVERAGE_LENGTH_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { eq: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { gte: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lt: ... } } }' instead.\\") + title_AVERAGE_LENGTH_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'title: { averageLength: { lte: ... } } }' instead.\\") + title_LONGEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { eq: ... } } }' instead.\\") + title_LONGEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gt: ... } } }' instead.\\") + title_LONGEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { gte: ... } } }' instead.\\") + title_LONGEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lt: ... } } }' instead.\\") + title_LONGEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { longestLength: { lte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { eq: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { gte: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lt: ... } } }' instead.\\") + title_SHORTEST_LENGTH_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'title: { shortestLength: { lte: ... } } }' instead.\\") } type PersonMoviesRelationship { @@ -1472,7 +1522,6 @@ describe("Union Interface Relationships", () => { input PersonMoviesUpdateFieldInput { connect: [PersonMoviesConnectFieldInput!] - connectOrCreate: [PersonMoviesConnectOrCreateFieldInput!] create: [PersonMoviesCreateFieldInput!] delete: [PersonMoviesDeleteFieldInput!] disconnect: [PersonMoviesDisconnectFieldInput!] @@ -1480,22 +1529,6 @@ describe("Union Interface Relationships", () => { where: PersonMoviesConnectionWhere } - input PersonOnCreateInput { - id: Int - name: String! - reputation: Int! - reviewerId: Int - } - - input PersonOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more PersonSort objects to sort People by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [PersonSort!] - } - \\"\\"\\" Fields to sort People by. The order in which sorts are applied is not guaranteed when specifying many fields in one PersonSort object. \\"\\"\\" @@ -1506,114 +1539,103 @@ describe("Union Interface Relationships", () => { reviewerId: SortDirection } - input PersonUniqueWhere { - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reviewerId_EQ: Int - } - input PersonUpdateInput { - id: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - id_DECREMENT: Int - id_INCREMENT: Int - id_SET: Int + id: IntScalarMutations + id_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'id: { decrement: ... } }' instead.\\") + id_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'id: { increment: ... } }' instead.\\") + id_SET: Int @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") movies: [PersonMoviesUpdateFieldInput!] - name: String @deprecated(reason: \\"Please use the explicit _SET field\\") - name_SET: String - reputation: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reputation_DECREMENT: Int - reputation_INCREMENT: Int - reputation_SET: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reviewerId_DECREMENT: Int - reviewerId_INCREMENT: Int - reviewerId_SET: Int + name: StringScalarMutations + name_SET: String @deprecated(reason: \\"Please use the generic mutation 'name: { set: ... } }' instead.\\") + reputation: IntScalarMutations + reputation_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { decrement: ... } }' instead.\\") + reputation_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { increment: ... } }' instead.\\") + reputation_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reputation: { set: ... } }' instead.\\") + reviewerId: IntScalarMutations + reviewerId_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { decrement: ... } }' instead.\\") + reviewerId_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { increment: ... } }' instead.\\") + reviewerId_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reviewerId: { set: ... } }' instead.\\") } input PersonWhere { AND: [PersonWhere!] NOT: PersonWhere OR: [PersonWhere!] - id: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_EQ: Int - id_GT: Int - id_GTE: Int - id_IN: [Int] - id_LT: Int - id_LTE: Int + id: IntScalarFilters + id_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_GT: Int @deprecated(reason: \\"Please use the relevant generic filter id: { gt: ... }\\") + id_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter id: { gte: ... }\\") + id_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_LT: Int @deprecated(reason: \\"Please use the relevant generic filter id: { lt: ... }\\") + id_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter id: { lte: ... }\\") + movies: MovieRelationshipFilters moviesAggregate: PersonMoviesAggregateInput + moviesConnection: PersonMoviesConnectionFilters \\"\\"\\" Return People where all of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_ALL: PersonMoviesConnectionWhere + moviesConnection_ALL: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return People where none of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_NONE: PersonMoviesConnectionWhere + moviesConnection_NONE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return People where one of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SINGLE: PersonMoviesConnectionWhere + moviesConnection_SINGLE: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return People where some of the related PersonMoviesConnections match this filter \\"\\"\\" - moviesConnection_SOME: PersonMoviesConnectionWhere + moviesConnection_SOME: PersonMoviesConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'moviesConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return People where all of the related Movies match this filter\\"\\"\\" - movies_ALL: MovieWhere + movies_ALL: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { all: ... }' instead.\\") \\"\\"\\"Return People where none of the related Movies match this filter\\"\\"\\" - movies_NONE: MovieWhere + movies_NONE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { none: ... }' instead.\\") \\"\\"\\"Return People where one of the related Movies match this filter\\"\\"\\" - movies_SINGLE: MovieWhere + movies_SINGLE: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { single: ... }' instead.\\") \\"\\"\\"Return People where some of the related Movies match this filter\\"\\"\\" - movies_SOME: MovieWhere - name: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - name_CONTAINS: String - name_ENDS_WITH: String - name_EQ: String - name_IN: [String!] - name_STARTS_WITH: String - reputation: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reputation_EQ: Int - reputation_GT: Int - reputation_GTE: Int - reputation_IN: [Int!] - reputation_LT: Int - reputation_LTE: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reviewerId_EQ: Int - reviewerId_GT: Int - reviewerId_GTE: Int - reviewerId_IN: [Int] - reviewerId_LT: Int - reviewerId_LTE: Int + movies_SOME: MovieWhere @deprecated(reason: \\"Please use the relevant generic filter 'movies: { some: ... }' instead.\\") + name: StringScalarFilters + name_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter name: { contains: ... }\\") + name_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { endsWith: ... }\\") + name_EQ: String @deprecated(reason: \\"Please use the relevant generic filter name: { eq: ... }\\") + name_IN: [String!] @deprecated(reason: \\"Please use the relevant generic filter name: { in: ... }\\") + name_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter name: { startsWith: ... }\\") + reputation: IntScalarFilters + reputation_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { eq: ... }\\") + reputation_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gt: ... }\\") + reputation_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gte: ... }\\") + reputation_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter reputation: { in: ... }\\") + reputation_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lt: ... }\\") + reputation_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lte: ... }\\") + reviewerId: IntScalarFilters + reviewerId_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { eq: ... }\\") + reviewerId_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gt: ... }\\") + reviewerId_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gte: ... }\\") + reviewerId_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { in: ... }\\") + reviewerId_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lt: ... }\\") + reviewerId_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lte: ... }\\") } type Query { - actors(limit: Int, offset: Int, options: ActorOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ActorSort!], where: ActorWhere): [Actor!]! + actors(limit: Int, offset: Int, sort: [ActorSort!], where: ActorWhere): [Actor!]! actorsAggregate(where: ActorWhere): ActorAggregateSelection! actorsConnection(after: String, first: Int, sort: [ActorSort!], where: ActorWhere): ActorsConnection! - directors(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: DirectorWhere): [Director!]! - influencers(limit: Int, offset: Int, options: InfluencerOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [InfluencerSort!], where: InfluencerWhere): [Influencer!]! + directors(limit: Int, offset: Int, where: DirectorWhere): [Director!]! + influencers(limit: Int, offset: Int, sort: [InfluencerSort!], where: InfluencerWhere): [Influencer!]! influencersAggregate(where: InfluencerWhere): InfluencerAggregateSelection! influencersConnection(after: String, first: Int, sort: [InfluencerSort!], where: InfluencerWhere): InfluencersConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - people(limit: Int, offset: Int, options: PersonOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [PersonSort!], where: PersonWhere): [Person!]! + people(limit: Int, offset: Int, sort: [PersonSort!], where: PersonWhere): [Person!]! peopleAggregate(where: PersonWhere): PersonAggregateSelection! peopleConnection(after: String, first: Int, sort: [PersonSort!], where: PersonWhere): PeopleConnection! - reviewers(limit: Int, offset: Int, options: ReviewerOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [ReviewerSort!], where: ReviewerWhere): [Reviewer!]! + reviewers(limit: Int, offset: Int, sort: [ReviewerSort!], where: ReviewerWhere): [Reviewer!]! reviewersAggregate(where: ReviewerWhere): ReviewerAggregateSelection! reviewersConnection(after: String, first: Int, sort: [ReviewerSort!], where: ReviewerWhere): ReviewersConnection! } - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int - } - \\"\\"\\" The edge properties for the following fields: * Movie.reviewers @@ -1627,26 +1649,27 @@ describe("Union Interface Relationships", () => { AND: [ReviewAggregationWhereInput!] NOT: ReviewAggregationWhereInput OR: [ReviewAggregationWhereInput!] - score_AVERAGE_EQUAL: Float - score_AVERAGE_GT: Float - score_AVERAGE_GTE: Float - score_AVERAGE_LT: Float - score_AVERAGE_LTE: Float - score_MAX_EQUAL: Int - score_MAX_GT: Int - score_MAX_GTE: Int - score_MAX_LT: Int - score_MAX_LTE: Int - score_MIN_EQUAL: Int - score_MIN_GT: Int - score_MIN_GTE: Int - score_MIN_LT: Int - score_MIN_LTE: Int - score_SUM_EQUAL: Int - score_SUM_GT: Int - score_SUM_GTE: Int - score_SUM_LT: Int - score_SUM_LTE: Int + score: IntScalarAggregationFilters + score_AVERAGE_EQUAL: Float @deprecated(reason: \\"Please use the relevant generic filter 'score: { average: { eq: ... } } }' instead.\\") + score_AVERAGE_GT: Float @deprecated(reason: \\"Please use the relevant generic filter 'score: { average: { gt: ... } } }' instead.\\") + score_AVERAGE_GTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'score: { average: { gte: ... } } }' instead.\\") + score_AVERAGE_LT: Float @deprecated(reason: \\"Please use the relevant generic filter 'score: { average: { lt: ... } } }' instead.\\") + score_AVERAGE_LTE: Float @deprecated(reason: \\"Please use the relevant generic filter 'score: { average: { lte: ... } } }' instead.\\") + score_MAX_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { max: { eq: ... } } }' instead.\\") + score_MAX_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { max: { gt: ... } } }' instead.\\") + score_MAX_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { max: { gte: ... } } }' instead.\\") + score_MAX_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { max: { lt: ... } } }' instead.\\") + score_MAX_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { max: { lte: ... } } }' instead.\\") + score_MIN_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { min: { eq: ... } } }' instead.\\") + score_MIN_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { min: { gt: ... } } }' instead.\\") + score_MIN_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { min: { gte: ... } } }' instead.\\") + score_MIN_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { min: { lt: ... } } }' instead.\\") + score_MIN_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { min: { lte: ... } } }' instead.\\") + score_SUM_EQUAL: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { sum: { eq: ... } } }' instead.\\") + score_SUM_GT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { sum: { gt: ... } } }' instead.\\") + score_SUM_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { sum: { gte: ... } } }' instead.\\") + score_SUM_LT: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { sum: { lt: ... } } }' instead.\\") + score_SUM_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter 'score: { sum: { lte: ... } } }' instead.\\") } input ReviewCreateInput { @@ -1658,23 +1681,23 @@ describe("Union Interface Relationships", () => { } input ReviewUpdateInput { - score: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - score_DECREMENT: Int - score_INCREMENT: Int - score_SET: Int + score: IntScalarMutations + score_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'score: { decrement: ... } }' instead.\\") + score_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'score: { increment: ... } }' instead.\\") + score_SET: Int @deprecated(reason: \\"Please use the generic mutation 'score: { set: ... } }' instead.\\") } input ReviewWhere { AND: [ReviewWhere!] NOT: ReviewWhere OR: [ReviewWhere!] - score: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - score_EQ: Int - score_GT: Int - score_GTE: Int - score_IN: [Int!] - score_LT: Int - score_LTE: Int + score: IntScalarFilters + score_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter score: { eq: ... }\\") + score_GT: Int @deprecated(reason: \\"Please use the relevant generic filter score: { gt: ... }\\") + score_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter score: { gte: ... }\\") + score_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter score: { in: ... }\\") + score_LT: Int @deprecated(reason: \\"Please use the relevant generic filter score: { lt: ... }\\") + score_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter score: { lte: ... }\\") } interface Reviewer { @@ -1707,13 +1730,15 @@ describe("Union Interface Relationships", () => { Person } - input ReviewerOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more ReviewerSort objects to sort Reviewers by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [ReviewerSort!] + input ReviewerRelationshipFilters { + \\"\\"\\"Filter type where all of the related Reviewers match this filter\\"\\"\\" + all: ReviewerWhere + \\"\\"\\"Filter type where none of the related Reviewers match this filter\\"\\"\\" + none: ReviewerWhere + \\"\\"\\"Filter type where one of the related Reviewers match this filter\\"\\"\\" + single: ReviewerWhere + \\"\\"\\"Filter type where some of the related Reviewers match this filter\\"\\"\\" + some: ReviewerWhere } \\"\\"\\" @@ -1725,36 +1750,35 @@ describe("Union Interface Relationships", () => { } input ReviewerUpdateInput { - reputation: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reputation_DECREMENT: Int - reputation_INCREMENT: Int - reputation_SET: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _SET field\\") - reviewerId_DECREMENT: Int - reviewerId_INCREMENT: Int - reviewerId_SET: Int + reputation: IntScalarMutations + reputation_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { decrement: ... } }' instead.\\") + reputation_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reputation: { increment: ... } }' instead.\\") + reputation_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reputation: { set: ... } }' instead.\\") + reviewerId: IntScalarMutations + reviewerId_DECREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { decrement: ... } }' instead.\\") + reviewerId_INCREMENT: Int @deprecated(reason: \\"Please use the relevant generic mutation 'reviewerId: { increment: ... } }' instead.\\") + reviewerId_SET: Int @deprecated(reason: \\"Please use the generic mutation 'reviewerId: { set: ... } }' instead.\\") } input ReviewerWhere { AND: [ReviewerWhere!] NOT: ReviewerWhere OR: [ReviewerWhere!] - reputation: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reputation_EQ: Int - reputation_GT: Int - reputation_GTE: Int - reputation_IN: [Int!] - reputation_LT: Int - reputation_LTE: Int - reviewerId: Int @deprecated(reason: \\"Please use the explicit _EQ version\\") - reviewerId_EQ: Int - reviewerId_GT: Int - reviewerId_GTE: Int - reviewerId_IN: [Int] - reviewerId_LT: Int - reviewerId_LTE: Int + reputation: IntScalarFilters + reputation_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { eq: ... }\\") + reputation_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gt: ... }\\") + reputation_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { gte: ... }\\") + reputation_IN: [Int!] @deprecated(reason: \\"Please use the relevant generic filter reputation: { in: ... }\\") + reputation_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lt: ... }\\") + reputation_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reputation: { lte: ... }\\") + reviewerId: IntScalarFilters + reviewerId_EQ: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { eq: ... }\\") + reviewerId_GT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gt: ... }\\") + reviewerId_GTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { gte: ... }\\") + reviewerId_IN: [Int] @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { in: ... }\\") + reviewerId_LT: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lt: ... }\\") + reviewerId_LTE: Int @deprecated(reason: \\"Please use the relevant generic filter reviewerId: { lte: ... }\\") typename: [ReviewerImplementation!] - typename_IN: [ReviewerImplementation!] @deprecated(reason: \\"The typename_IN filter is deprecated, please use the typename filter instead\\") } type ReviewersConnection { @@ -1776,6 +1800,27 @@ describe("Union Interface Relationships", () => { shortest: String } + \\"\\"\\"Filters for an aggregation of a string field\\"\\"\\" + input StringScalarAggregationFilters { + averageLength: FloatScalarFilters + longestLength: IntScalarFilters + shortestLength: IntScalarFilters + } + + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + type UpdateActorsMutationResponse { actors: [Actor!]! info: UpdateInfo! diff --git a/packages/graphql/tests/schema/unions.test.ts b/packages/graphql/tests/schema/unions.test.ts index a72e80dcb0..104c4dfd91 100644 --- a/packages/graphql/tests/schema/unions.test.ts +++ b/packages/graphql/tests/schema/unions.test.ts @@ -78,7 +78,6 @@ describe("Unions", () => { type GenreAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input GenreConnectWhere { @@ -94,15 +93,6 @@ describe("Unions", () => { node: Genre! } - input GenreOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more GenreSort objects to sort Genres by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [GenreSort!] - } - \\"\\"\\" Fields to sort Genres by. The order in which sorts are applied is not guaranteed when specifying many fields in one GenreSort object. \\"\\"\\" @@ -111,20 +101,20 @@ describe("Unions", () => { } input GenreUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") } input GenreWhere { AND: [GenreWhere!] NOT: GenreWhere OR: [GenreWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") } type GenresConnection { @@ -133,21 +123,29 @@ describe("Unions", () => { totalCount: Int! } - type IDAggregateSelection { - longest: ID - shortest: ID + \\"\\"\\"ID filters\\"\\"\\" + input IDScalarFilters { + contains: ID + endsWith: ID + eq: ID + in: [ID!] + startsWith: ID + } + + \\"\\"\\"ID mutations\\"\\"\\" + input IDScalarMutations { + set: ID } type Movie { id: ID - search(directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - searchConnection(after: String, directed: Boolean = true @deprecated(reason: \\"The directed argument is deprecated, and the direction of the field will be configured in the GraphQL server\\"), first: Int, where: MovieSearchConnectionWhere): MovieSearchConnection! + search(limit: Int, offset: Int, where: SearchWhere): [Search!]! + searchConnection(after: String, first: Int, where: MovieSearchConnectionWhere): MovieSearchConnection! searchNoDirective: Search } type MovieAggregateSelection { count: Int! - id: IDAggregateSelection! @deprecated(reason: \\"aggregation of ID fields are deprecated and will be removed\\") } input MovieConnectInput { @@ -176,15 +174,6 @@ describe("Unions", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - input MovieSearchConnectInput { Genre: [MovieSearchGenreConnectFieldInput!] Movie: [MovieSearchMovieConnectFieldInput!] @@ -196,6 +185,25 @@ describe("Unions", () => { totalCount: Int! } + input MovieSearchConnectionFilters { + \\"\\"\\" + Return Movies where all of the related MovieSearchConnections match this filter + \\"\\"\\" + all: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where none of the related MovieSearchConnections match this filter + \\"\\"\\" + none: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where one of the related MovieSearchConnections match this filter + \\"\\"\\" + single: MovieSearchConnectionWhere + \\"\\"\\" + Return Movies where some of the related MovieSearchConnections match this filter + \\"\\"\\" + some: MovieSearchConnectionWhere + } + input MovieSearchConnectionWhere { Genre: MovieSearchGenreConnectionWhere Movie: MovieSearchMovieConnectionWhere @@ -319,8 +327,8 @@ describe("Unions", () => { } input MovieUpdateInput { - id: ID @deprecated(reason: \\"Please use the explicit _SET field\\") - id_SET: ID + id: IDScalarMutations + id_SET: ID @deprecated(reason: \\"Please use the generic mutation 'id: { set: ... } }' instead.\\") search: MovieSearchUpdateInput } @@ -328,36 +336,38 @@ describe("Unions", () => { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - id: ID @deprecated(reason: \\"Please use the explicit _EQ version\\") - id_CONTAINS: ID - id_ENDS_WITH: ID - id_EQ: ID - id_IN: [ID] - id_STARTS_WITH: ID + id: IDScalarFilters + id_CONTAINS: ID @deprecated(reason: \\"Please use the relevant generic filter id: { contains: ... }\\") + id_ENDS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { endsWith: ... }\\") + id_EQ: ID @deprecated(reason: \\"Please use the relevant generic filter id: { eq: ... }\\") + id_IN: [ID] @deprecated(reason: \\"Please use the relevant generic filter id: { in: ... }\\") + id_STARTS_WITH: ID @deprecated(reason: \\"Please use the relevant generic filter id: { startsWith: ... }\\") + search: SearchRelationshipFilters + searchConnection: MovieSearchConnectionFilters \\"\\"\\" Return Movies where all of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_ALL: MovieSearchConnectionWhere + searchConnection_ALL: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { all: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where none of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_NONE: MovieSearchConnectionWhere + searchConnection_NONE: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { none: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where one of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_SINGLE: MovieSearchConnectionWhere + searchConnection_SINGLE: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { single: { node: ... } } }' instead.\\") \\"\\"\\" Return Movies where some of the related MovieSearchConnections match this filter \\"\\"\\" - searchConnection_SOME: MovieSearchConnectionWhere + searchConnection_SOME: MovieSearchConnectionWhere @deprecated(reason: \\"Please use the relevant generic filter 'searchConnection: { some: { node: ... } } }' instead.\\") \\"\\"\\"Return Movies where all of the related Searches match this filter\\"\\"\\" - search_ALL: SearchWhere + search_ALL: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { all: ... }' instead.\\") \\"\\"\\"Return Movies where none of the related Searches match this filter\\"\\"\\" - search_NONE: SearchWhere + search_NONE: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { none: ... }' instead.\\") \\"\\"\\"Return Movies where one of the related Searches match this filter\\"\\"\\" - search_SINGLE: SearchWhere + search_SINGLE: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { single: ... }' instead.\\") \\"\\"\\"Return Movies where some of the related Searches match this filter\\"\\"\\" - search_SOME: SearchWhere + search_SOME: SearchWhere @deprecated(reason: \\"Please use the relevant generic filter 'search: { some: ... }' instead.\\") } type MoviesConnection { @@ -384,23 +394,28 @@ describe("Unions", () => { } type Query { - genres(limit: Int, offset: Int, options: GenreOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [GenreSort!], where: GenreWhere): [Genre!]! + genres(limit: Int, offset: Int, sort: [GenreSort!], where: GenreWhere): [Genre!]! genresAggregate(where: GenreWhere): GenreAggregateSelection! genresConnection(after: String, first: Int, sort: [GenreSort!], where: GenreWhere): GenresConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - searches(limit: Int, offset: Int, options: QueryOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), where: SearchWhere): [Search!]! - } - - \\"\\"\\"Input type for options that can be specified on a query operation.\\"\\"\\" - input QueryOptions { - limit: Int - offset: Int + searches(limit: Int, offset: Int, where: SearchWhere): [Search!]! } union Search = Genre | Movie + input SearchRelationshipFilters { + \\"\\"\\"Filter type where all of the related Searches match this filter\\"\\"\\" + all: SearchWhere + \\"\\"\\"Filter type where none of the related Searches match this filter\\"\\"\\" + none: SearchWhere + \\"\\"\\"Filter type where one of the related Searches match this filter\\"\\"\\" + single: SearchWhere + \\"\\"\\"Filter type where some of the related Searches match this filter\\"\\"\\" + some: SearchWhere + } + input SearchWhere { Genre: GenreWhere Movie: MovieWhere diff --git a/packages/graphql/tests/schema/vector.test.ts b/packages/graphql/tests/schema/vector.test.ts index 0ada3210a5..8ead638dc1 100644 --- a/packages/graphql/tests/schema/vector.test.ts +++ b/packages/graphql/tests/schema/vector.test.ts @@ -99,64 +99,55 @@ describe("@vector schema", () => { node: Movie! } - input MovieOptions { - limit: Int - offset: Int - \\"\\"\\" - Specify one or more MovieSort objects to sort Movies by. The sorts will be applied in the order in which they are arranged in the array. - \\"\\"\\" - sort: [MovieSort!] - } - - \\"\\"\\" - Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. - \\"\\"\\" - input MovieSort { - description: SortDirection - title: SortDirection - } - - input MovieUpdateInput { - description: String @deprecated(reason: \\"Please use the explicit _SET field\\") - description_SET: String - title: String @deprecated(reason: \\"Please use the explicit _SET field\\") - title_SET: String - } - - type MovieVectorEdge { + type MovieIndexEdge { cursor: String! node: Movie! score: Float! } \\"\\"\\"The input for sorting a Vector query on an index of Movie\\"\\"\\" - input MovieVectorSort { + input MovieIndexSort { node: MovieSort score: SortDirection } \\"\\"\\"The input for filtering a Vector query on an index of Movie\\"\\"\\" - input MovieVectorWhere { + input MovieIndexWhere { node: MovieWhere score: FloatWhere } + \\"\\"\\" + Fields to sort Movies by. The order in which sorts are applied is not guaranteed when specifying many fields in one MovieSort object. + \\"\\"\\" + input MovieSort { + description: SortDirection + title: SortDirection + } + + input MovieUpdateInput { + description: StringScalarMutations + description_SET: String @deprecated(reason: \\"Please use the generic mutation 'description: { set: ... } }' instead.\\") + title: StringScalarMutations + title_SET: String @deprecated(reason: \\"Please use the generic mutation 'title: { set: ... } }' instead.\\") + } + input MovieWhere { AND: [MovieWhere!] NOT: MovieWhere OR: [MovieWhere!] - description: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - description_CONTAINS: String - description_ENDS_WITH: String - description_EQ: String - description_IN: [String] - description_STARTS_WITH: String - title: String @deprecated(reason: \\"Please use the explicit _EQ version\\") - title_CONTAINS: String - title_ENDS_WITH: String - title_EQ: String - title_IN: [String] - title_STARTS_WITH: String + description: StringScalarFilters + description_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter description: { contains: ... }\\") + description_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { endsWith: ... }\\") + description_EQ: String @deprecated(reason: \\"Please use the relevant generic filter description: { eq: ... }\\") + description_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter description: { in: ... }\\") + description_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter description: { startsWith: ... }\\") + title: StringScalarFilters + title_CONTAINS: String @deprecated(reason: \\"Please use the relevant generic filter title: { contains: ... }\\") + title_ENDS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { endsWith: ... }\\") + title_EQ: String @deprecated(reason: \\"Please use the relevant generic filter title: { eq: ... }\\") + title_IN: [String] @deprecated(reason: \\"Please use the relevant generic filter title: { in: ... }\\") + title_STARTS_WITH: String @deprecated(reason: \\"Please use the relevant generic filter title: { startsWith: ... }\\") } type MoviesConnection { @@ -165,8 +156,8 @@ describe("@vector schema", () => { totalCount: Int! } - type MoviesVectorConnection { - edges: [MovieVectorEdge!]! + type MoviesIndexConnection { + edges: [MovieIndexEdge!]! pageInfo: PageInfo! totalCount: Int! } @@ -186,11 +177,11 @@ describe("@vector schema", () => { } type Query { - descriptionQuery(after: String, first: Int, sort: [MovieVectorSort!], vector: [Float!], where: MovieVectorWhere): MoviesVectorConnection! - movies(limit: Int, offset: Int, options: MovieOptions @deprecated(reason: \\"Query options argument is deprecated, please use pagination arguments like limit, offset and sort instead.\\"), sort: [MovieSort!], where: MovieWhere): [Movie!]! + descriptionQuery(after: String, first: Int, sort: [MovieIndexSort!], vector: [Float!], where: MovieIndexWhere): MoviesIndexConnection! + movies(limit: Int, offset: Int, sort: [MovieSort!], where: MovieWhere): [Movie!]! moviesAggregate(where: MovieWhere): MovieAggregateSelection! moviesConnection(after: String, first: Int, sort: [MovieSort!], where: MovieWhere): MoviesConnection! - titleQuery(after: String, first: Int, sort: [MovieVectorSort!], vector: [Float!], where: MovieVectorWhere): MoviesVectorConnection! + titleQuery(after: String, first: Int, sort: [MovieIndexSort!], vector: [Float!], where: MovieIndexWhere): MoviesIndexConnection! } \\"\\"\\"An enum for sorting in either ascending or descending order.\\"\\"\\" @@ -206,6 +197,20 @@ describe("@vector schema", () => { shortest: String } + \\"\\"\\"String filters\\"\\"\\" + input StringScalarFilters { + contains: String + endsWith: String + eq: String + in: [String!] + startsWith: String + } + + \\"\\"\\"String mutations\\"\\"\\" + input StringScalarMutations { + set: String + } + \\"\\"\\" Information about the number of nodes and relationships created and deleted during an update mutation \\"\\"\\" diff --git a/packages/graphql/tests/tck/advanced-filtering.test.ts b/packages/graphql/tests/tck/advanced-filtering.test.ts index fccc7bc159..e686df3b84 100644 --- a/packages/graphql/tests/tck/advanced-filtering.test.ts +++ b/packages/graphql/tests/tck/advanced-filtering.test.ts @@ -60,10 +60,10 @@ describe("Cypher Advanced Filtering", () => { }); }); - test("implicit EQ", async () => { + test("equals", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "The Matrix" }) { + movies(where: { title: { eq: "The Matrix" } }) { title } } @@ -72,7 +72,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -84,34 +85,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("EQ", async () => { + test("in", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "The Matrix" }) { - title - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.title = $param0 - RETURN this { .title } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"The Matrix\\" - }" - `); - }); - - test("IN", async () => { - const query = /* GraphQL */ ` - { - movies(where: { _id_IN: ["123"] }) { + movies(where: { _id: { in: ["123"] } }) { _id } } @@ -120,7 +97,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this._id IN $param0 RETURN this { ._id } AS this" `); @@ -134,10 +112,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("REGEX", async () => { + test("matches", async () => { const query = /* GraphQL */ ` { - movies(where: { id_MATCHES: "(?i)123.*" }) { + movies(where: { id: { matches: "(?i)123.*" } }) { id } } @@ -146,7 +124,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id =~ $param0 RETURN this { .id } AS this" `); @@ -161,7 +140,7 @@ describe("Cypher Advanced Filtering", () => { test("NOT", async () => { const query = /* GraphQL */ ` { - movies(where: { NOT: { id_EQ: "123" } }) { + movies(where: { NOT: { id: { eq: "123" } } }) { id } } @@ -170,7 +149,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (this.id = $param0) RETURN this { .id } AS this" `); @@ -182,10 +162,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("CONTAINS", async () => { + test("contains", async () => { const query = /* GraphQL */ ` { - movies(where: { id_CONTAINS: "123" }) { + movies(where: { id: { contains: "123" } }) { id } } @@ -194,7 +174,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id CONTAINS $param0 RETURN this { .id } AS this" `); @@ -206,10 +187,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("STARTS_WITH", async () => { + test("startsWith", async () => { const query = /* GraphQL */ ` { - movies(where: { id_STARTS_WITH: "123" }) { + movies(where: { id: { startsWith: "123" } }) { id } } @@ -218,7 +199,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id STARTS WITH $param0 RETURN this { .id } AS this" `); @@ -230,10 +212,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("ENDS_WITH", async () => { + test("endsWith", async () => { const query = /* GraphQL */ ` { - movies(where: { id_ENDS_WITH: "123" }) { + movies(where: { id: { endsWith: "123" } }) { id } } @@ -242,7 +224,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id ENDS WITH $param0 RETURN this { .id } AS this" `); @@ -254,10 +237,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LT", async () => { + test("lessThan", async () => { const query = /* GraphQL */ ` { - movies(where: { actorCount_LT: 123 }) { + movies(where: { actorCount: { lt: 123 } }) { actorCount } } @@ -266,7 +249,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.actorCount < $param0 RETURN this { .actorCount } AS this" `); @@ -281,10 +265,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LT BigInt", async () => { + test("lessThan BigInt", async () => { const query = /* GraphQL */ ` { - movies(where: { budget_LT: 9223372036854775807 }) { + movies(where: { budget: { lt: 9223372036854775807 } }) { budget } } @@ -293,7 +277,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.budget < $param0 RETURN this { .budget } AS this" `); @@ -308,10 +293,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LT String", async () => { + test("lessThan String", async () => { const query = /* GraphQL */ ` { - movies(where: { title_LT: "The Matrix Revolutions" }) { + movies(where: { title: { lt: "The Matrix Revolutions" } }) { title } } @@ -319,7 +304,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title < $param0 RETURN this { .title } AS this" `); @@ -331,10 +317,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LTE", async () => { + test("lessThanEquals", async () => { const query = /* GraphQL */ ` { - movies(where: { actorCount_LTE: 123 }) { + movies(where: { actorCount: { lte: 123 } }) { actorCount } } @@ -343,7 +329,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.actorCount <= $param0 RETURN this { .actorCount } AS this" `); @@ -358,10 +345,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LTE BigInt", async () => { + test("lessThanEquals BigInt", async () => { const query = /* GraphQL */ ` { - movies(where: { budget_LTE: 9223372036854775807 }) { + movies(where: { budget: { lte: 9223372036854775807 } }) { budget } } @@ -370,7 +357,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.budget <= $param0 RETURN this { .budget } AS this" `); @@ -385,10 +373,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("LTE String", async () => { + test("lessThanEquals String", async () => { const query = /* GraphQL */ ` { - movies(where: { title_LTE: "The Matrix Revolutions" }) { + movies(where: { title: { lte: "The Matrix Revolutions" } }) { title } } @@ -396,7 +384,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title <= $param0 RETURN this { .title } AS this" `); @@ -408,10 +397,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GT", async () => { + test("greaterThan", async () => { const query = /* GraphQL */ ` { - movies(where: { actorCount_GT: 123 }) { + movies(where: { actorCount: { gt: 123 } }) { actorCount } } @@ -420,7 +409,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.actorCount > $param0 RETURN this { .actorCount } AS this" `); @@ -435,10 +425,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GT BigInt", async () => { + test("greaterThan BigInt", async () => { const query = /* GraphQL */ ` { - movies(where: { budget_GT: 9223372036854775000 }) { + movies(where: { budget: { gt: 9223372036854775000 } }) { budget } } @@ -447,7 +437,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.budget > $param0 RETURN this { .budget } AS this" `); @@ -462,10 +453,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GT String", async () => { + test("greaterThan String", async () => { const query = /* GraphQL */ ` { - movies(where: { title_GT: "The Matrix Revolutions" }) { + movies(where: { title: { gt: "The Matrix Revolutions" } }) { title } } @@ -473,7 +464,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title > $param0 RETURN this { .title } AS this" `); @@ -485,10 +477,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GTE", async () => { + test("greaterThanEquals", async () => { const query = /* GraphQL */ ` { - movies(where: { actorCount_GTE: 123 }) { + movies(where: { actorCount: { gte: 123 } }) { actorCount } } @@ -497,7 +489,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.actorCount >= $param0 RETURN this { .actorCount } AS this" `); @@ -512,10 +505,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GTE BigInt", async () => { + test("greaterThanEquals BigInt", async () => { const query = /* GraphQL */ ` { - movies(where: { budget_GTE: 9223372036854775000 }) { + movies(where: { budget: { gte: 9223372036854775000 } }) { budget } } @@ -524,7 +517,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.budget >= $param0 RETURN this { .budget } AS this" `); @@ -539,10 +533,10 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("GTE String", async () => { + test("greaterThanEquals String", async () => { const query = /* GraphQL */ ` { - movies(where: { title_GTE: "The Matrix Revolutions" }) { + movies(where: { title: { gte: "The Matrix Revolutions" } }) { title } } @@ -550,7 +544,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title >= $param0 RETURN this { .title } AS this" `); @@ -566,7 +561,7 @@ describe("Cypher Advanced Filtering", () => { test("equality", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { name_EQ: "some genre" } }) { + movies(where: { genres: { some: { name: { eq: "some genre" } } } }) { actorCount } } @@ -575,7 +570,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE EXISTS { MATCH (this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 @@ -590,51 +586,25 @@ describe("Cypher Advanced Filtering", () => { `); }); - test("NONE", async () => { - const query = /* GraphQL */ ` - { - movies(where: { genres_NONE: { name_EQ: "some genre" } }) { - actorCount - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE NOT (EXISTS { - MATCH (this)-[:IN_GENRE]->(this0:Genre) - WHERE this0.name = $param0 - }) - RETURN this { .actorCount } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"some genre\\" - }" - `); - }); - describe("List Predicates", () => { - const generateQuery = (operator: "ALL" | "NONE" | "SINGLE" | "SOME"): string => { + const generateQuery = (operator: "all" | "none" | "single" | "some"): string => { const query = /* GraphQL */ ` { - movies(where: { genres_${operator}: { name_EQ: "some genre" } }) { + movies(where: { genres: { ${operator}: { name: { eq: "some genre" } } } }) { actorCount } } `; return query; }; - test("ALL", async () => { - const query = generateQuery("ALL"); + test("all", async () => { + const query = generateQuery("all"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (EXISTS { MATCH (this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 @@ -650,13 +620,15 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("NONE", async () => { - const query = generateQuery("NONE"); + + test("none", async () => { + const query = generateQuery("none"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (EXISTS { MATCH (this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 @@ -669,13 +641,15 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("SINGLE", async () => { - const query = generateQuery("SINGLE"); + + test("single", async () => { + const query = generateQuery("single"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE single(this0 IN [(this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 | 1] WHERE true) RETURN this { .actorCount } AS this" `); @@ -685,13 +659,14 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("SOME", async () => { - const query = generateQuery("SOME"); + test("some", async () => { + const query = generateQuery("some"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE EXISTS { MATCH (this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 @@ -711,7 +686,7 @@ describe("Cypher Advanced Filtering", () => { test("Node and relationship properties equality", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SOME: { node: { name_EQ: "some genre" } } }) { + movies(where: { genresConnection: { some: { node: { name: { eq: "some genre" } } } } }) { actorCount } } @@ -720,7 +695,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 @@ -738,7 +714,7 @@ describe("Cypher Advanced Filtering", () => { test("Node and relationship properties NONE", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_NONE: { node: { name_EQ: "some genre" } } }) { + movies(where: { genresConnection: { none: { node: { name: { eq: "some genre" } } } } }) { actorCount } } @@ -747,7 +723,8 @@ describe("Cypher Advanced Filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 @@ -763,23 +740,24 @@ describe("Cypher Advanced Filtering", () => { }); describe("List Predicates", () => { - const generateQuery = (operator: "ALL" | "NONE" | "SINGLE" | "SOME"): string => { + const generateQuery = (operator: "all" | "none" | "single" | "some"): string => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_${operator}: { node: { name_EQ: "some genre" } } }) { + movies(where: { genresConnection: { ${operator}: { node: { name: { eq: "some genre" }} } } }) { actorCount } } `; return query; }; - test("ALL", async () => { - const query = generateQuery("ALL"); + test("all", async () => { + const query = generateQuery("all"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 @@ -795,13 +773,14 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("NONE", async () => { - const query = generateQuery("NONE"); + test("none", async () => { + const query = generateQuery("none"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 @@ -814,13 +793,14 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("SINGLE", async () => { - const query = generateQuery("SINGLE"); + test("single", async () => { + const query = generateQuery("single"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE single(this0 IN [(this)-[this1:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 | 1] WHERE true) RETURN this { .actorCount } AS this" `); @@ -830,13 +810,14 @@ describe("Cypher Advanced Filtering", () => { }" `); }); - test("SOME", async () => { - const query = generateQuery("SOME"); + test("some", async () => { + const query = generateQuery("some"); const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 diff --git a/packages/graphql/tests/tck/aggregations/alias-directive.test.ts b/packages/graphql/tests/tck/aggregations/alias-directive.test.ts index c47e4dd4a6..8568717cf7 100644 --- a/packages/graphql/tests/tck/aggregations/alias-directive.test.ts +++ b/packages/graphql/tests/tck/aggregations/alias-directive.test.ts @@ -43,10 +43,6 @@ describe("Cypher Aggregations Many with Alias directive", () => { const query = /* GraphQL */ ` { moviesAggregate { - id { - shortest - longest - } title { shortest longest @@ -67,26 +63,23 @@ describe("Cypher Aggregations Many with Alias directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN { shortest: min(this._id), longest: max(this._id) } AS var0 - } + "CYPHER 5 CALL { MATCH (this:Movie) WITH this ORDER BY size(this._title) DESC WITH collect(this._title) AS list - RETURN { longest: head(list), shortest: last(list) } AS var1 + RETURN { longest: head(list), shortest: last(list) } AS var0 } CALL { MATCH (this:Movie) - RETURN { min: min(this.\`_imdb Rating\`), max: max(this.\`_imdb Rating\`), average: avg(this.\`_imdb Rating\`) } AS var2 + RETURN { min: min(this.\`_imdb Rating\`), max: max(this.\`_imdb Rating\`), average: avg(this.\`_imdb Rating\`) } AS var1 } CALL { MATCH (this:Movie) - RETURN { min: apoc.date.convertFormat(toString(min(this._createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this._createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var3 + RETURN { min: apoc.date.convertFormat(toString(min(this._createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this._createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var2 } - RETURN { id: var0, title: var1, imdbRating: var2, createdAt: var3 }" + RETURN { title: var0, imdbRating: var1, createdAt: var2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/aggregations/alias.test.ts b/packages/graphql/tests/tck/aggregations/alias.test.ts index c25a93322d..4edc9c337d 100644 --- a/packages/graphql/tests/tck/aggregations/alias.test.ts +++ b/packages/graphql/tests/tck/aggregations/alias.test.ts @@ -44,10 +44,6 @@ describe("Cypher Aggregations Many while Alias fields", () => { { moviesAggregate { _count: count - _id: id { - _shortest: shortest - _longest: longest - } _title: title { _shortest: shortest _longest: longest @@ -68,30 +64,27 @@ describe("Cypher Aggregations Many while Alias fields", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN count(this) AS var0 - } + "CYPHER 5 CALL { MATCH (this:Movie) - RETURN { _shortest: min(this.id), _longest: max(this.id) } AS var1 + RETURN count(this) AS var0 } CALL { MATCH (this:Movie) WITH this ORDER BY size(this.title) DESC WITH collect(this.title) AS list - RETURN { _longest: head(list), _shortest: last(list) } AS var2 + RETURN { _longest: head(list), _shortest: last(list) } AS var1 } CALL { MATCH (this:Movie) - RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var3 + RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var2 } CALL { MATCH (this:Movie) - RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var4 + RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var3 } - RETURN { _count: var0, _id: var1, _title: var2, _imdbRating: var3, _createdAt: var4 }" + RETURN { _count: var0, _title: var1, _imdbRating: var2, _createdAt: var3 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/aggregations/auth.test.ts b/packages/graphql/tests/tck/aggregations/auth.test.ts index d16eb2e362..9bd0a1bd1c 100644 --- a/packages/graphql/tests/tck/aggregations/auth.test.ts +++ b/packages/graphql/tests/tck/aggregations/auth.test.ts @@ -31,34 +31,34 @@ describe("Cypher Aggregations with Auth", () => { type User @node { id: ID @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) name: String! @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) imdbRatingInt: Int! @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) imdbRatingFloat: Float! @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) imdbRatingBigInt: BigInt! @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) createdAt: DateTime @authorization( - validate: [{ operations: [READ], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [READ], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) } extend type User @authorization( - validate: [{ operations: [AGGREGATE], when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }] - filter: [{ operations: [AGGREGATE], where: { node: { id_EQ: "$jwt.sub" } } }] + validate: [{ operations: [AGGREGATE], when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }] + filter: [{ operations: [AGGREGATE], where: { node: { id: { eq: "$jwt.sub" } } } }] ) `; @@ -81,7 +81,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(this) AS var0 @@ -103,7 +104,7 @@ describe("Cypher Aggregations with Auth", () => { test("Count with WHERE", async () => { const query = /* GraphQL */ ` { - usersAggregate(where: { name_EQ: "some-name" }) { + usersAggregate(where: { name: { eq: "some-name" } }) { count } } @@ -113,7 +114,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (this.name = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(this) AS var0 @@ -149,7 +151,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN { min: min(this.imdbRatingInt), max: max(this.imdbRatingInt) } AS var0 @@ -184,7 +187,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN { min: min(this.imdbRatingFloat), max: max(this.imdbRatingFloat) } AS var0 @@ -219,7 +223,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN { min: min(this.imdbRatingBigInt), max: max(this.imdbRatingBigInt) } AS var0 @@ -238,41 +243,6 @@ describe("Cypher Aggregations with Auth", () => { `); }); - test("Field ID with auth", async () => { - const query = /* GraphQL */ ` - { - usersAggregate { - id { - shortest - longest - } - } - } - `; - - const token = createBearerToken("secret", { sub: "super_admin" }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:User) - WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - RETURN { shortest: min(this.id), longest: max(this.id) } AS var0 - } - RETURN { id: var0 }" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [], - \\"sub\\": \\"super_admin\\" - } - }" - `); - }); - test("Field String with auth", async () => { const query = /* GraphQL */ ` { @@ -289,7 +259,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this @@ -327,7 +298,8 @@ describe("Cypher Aggregations with Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:User) WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN { min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var0 diff --git a/packages/graphql/tests/tck/aggregations/bigint.test.ts b/packages/graphql/tests/tck/aggregations/bigint.test.ts index c8ea500b29..15c60f0228 100644 --- a/packages/graphql/tests/tck/aggregations/bigint.test.ts +++ b/packages/graphql/tests/tck/aggregations/bigint.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:File) RETURN { min: min(this.size) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:File) RETURN { max: max(this.size) } AS var0 } @@ -98,7 +100,8 @@ describe("Cypher Aggregations BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:File) RETURN { average: avg(this.size) } AS var0 } @@ -122,7 +125,8 @@ describe("Cypher Aggregations BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:File) RETURN { sum: sum(this.size) } AS var0 } @@ -149,7 +153,8 @@ describe("Cypher Aggregations BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:File) RETURN { min: min(this.size), max: max(this.size), average: avg(this.size), sum: sum(this.size) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/count.test.ts b/packages/graphql/tests/tck/aggregations/count.test.ts index 4baf2ecc1b..4c4465055f 100644 --- a/packages/graphql/tests/tck/aggregations/count.test.ts +++ b/packages/graphql/tests/tck/aggregations/count.test.ts @@ -48,7 +48,8 @@ describe("Cypher Aggregations Count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN count(this) AS var0 } @@ -61,7 +62,7 @@ describe("Cypher Aggregations Count", () => { test("Count with WHERE", async () => { const query = /* GraphQL */ ` { - moviesAggregate(where: { title_EQ: "some-title" }) { + moviesAggregate(where: { title: { eq: "some-title" } }) { count } } @@ -70,7 +71,8 @@ describe("Cypher Aggregations Count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) WHERE this.title = $param0 RETURN count(this) AS var0 diff --git a/packages/graphql/tests/tck/aggregations/datetime.test.ts b/packages/graphql/tests/tck/aggregations/datetime.test.ts index 426612fb1a..80ae342332 100644 --- a/packages/graphql/tests/tck/aggregations/datetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/datetime.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var0 } @@ -99,7 +101,8 @@ describe("Cypher Aggregations DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/duration.test.ts b/packages/graphql/tests/tck/aggregations/duration.test.ts index fb4b33bed2..f512903504 100644 --- a/packages/graphql/tests/tck/aggregations/duration.test.ts +++ b/packages/graphql/tests/tck/aggregations/duration.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.screenTime) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.screenTime) } AS var0 } @@ -99,7 +101,8 @@ describe("Cypher Aggregations Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.screenTime), max: max(this.screenTime) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-alias.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-alias.test.ts index 2b71052e46..1cbb10d85f 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-alias.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-alias.test.ts @@ -65,7 +65,8 @@ describe("Field Level Aggregations Alias", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -98,7 +99,8 @@ describe("Field Level Aggregations Alias", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts index 7540c2e398..4e95e22c8c 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-edge.test.ts @@ -68,7 +68,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-labels.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-labels.test.ts index 8adbe2ca97..7cf61e1277 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-labels.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-labels.test.ts @@ -65,7 +65,8 @@ describe("Field Level Aggregations Alias", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-where.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-where.test.ts index 5eb36a7960..8dd6d5de01 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-where.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations-where.test.ts @@ -50,7 +50,7 @@ describe("Field Level Aggregations Where", () => { query { movies { title - actorsAggregate(where: { age_GT: 40 }) { + actorsAggregate(where: { age: { gt: 40 } }) { count } } @@ -60,7 +60,8 @@ describe("Field Level Aggregations Where", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) @@ -98,7 +99,8 @@ describe("Field Level Aggregations Where", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) diff --git a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts index 5237947c11..f14e151448 100644 --- a/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts +++ b/packages/graphql/tests/tck/aggregations/field-level-aggregations/field-level-aggregations.test.ts @@ -59,7 +59,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -91,7 +92,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -132,7 +134,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -164,7 +167,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -197,7 +201,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -234,7 +239,8 @@ describe("Field Level Aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/aggregations/float.test.ts b/packages/graphql/tests/tck/aggregations/float.test.ts index 0963745b80..0f509d0aa1 100644 --- a/packages/graphql/tests/tck/aggregations/float.test.ts +++ b/packages/graphql/tests/tck/aggregations/float.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.actorCount) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.actorCount) } AS var0 } @@ -98,7 +100,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { average: avg(this.actorCount) } AS var0 } @@ -122,7 +125,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { sum: sum(this.actorCount) } AS var0 } @@ -149,7 +153,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.actorCount), max: max(this.actorCount), average: avg(this.actorCount), sum: sum(this.actorCount) } AS var0 } @@ -177,7 +182,8 @@ describe("Cypher Aggregations Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN count(this) AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/id.test.ts b/packages/graphql/tests/tck/aggregations/id.test.ts deleted file mode 100644 index f38f77244b..0000000000 --- a/packages/graphql/tests/tck/aggregations/id.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Cypher Aggregations ID", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Min", async () => { - const query = /* GraphQL */ ` - { - moviesAggregate { - id { - shortest - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN { shortest: min(this.id) } AS var0 - } - RETURN { id: var0 }" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("Max", async () => { - const query = /* GraphQL */ ` - { - moviesAggregate { - id { - longest - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN { longest: max(this.id) } AS var0 - } - RETURN { id: var0 }" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("Min and Max", async () => { - const query = /* GraphQL */ ` - { - moviesAggregate { - id { - shortest - longest - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN { shortest: min(this.id), longest: max(this.id) } AS var0 - } - RETURN { id: var0 }" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/aggregations/int.test.ts b/packages/graphql/tests/tck/aggregations/int.test.ts index e5a6235edc..74bf1ee19c 100644 --- a/packages/graphql/tests/tck/aggregations/int.test.ts +++ b/packages/graphql/tests/tck/aggregations/int.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.imdbRating) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.imdbRating) } AS var0 } @@ -98,7 +100,8 @@ describe("Cypher Aggregations Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { average: avg(this.imdbRating) } AS var0 } @@ -122,7 +125,8 @@ describe("Cypher Aggregations Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { sum: sum(this.imdbRating) } AS var0 } @@ -149,7 +153,8 @@ describe("Cypher Aggregations Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.imdbRating), max: max(this.imdbRating), average: avg(this.imdbRating), sum: sum(this.imdbRating) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/label.test.ts b/packages/graphql/tests/tck/aggregations/label.test.ts index e7af1838f3..85746101e1 100644 --- a/packages/graphql/tests/tck/aggregations/label.test.ts +++ b/packages/graphql/tests/tck/aggregations/label.test.ts @@ -50,10 +50,6 @@ describe("Cypher Aggregations Many while Alias fields", () => { const query = /* GraphQL */ ` { moviesAggregate { - _id: id { - _shortest: shortest - _longest: longest - } _title: title { _shortest: shortest _longest: longest @@ -74,26 +70,23 @@ describe("Cypher Aggregations Many while Alias fields", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Film) - RETURN { _shortest: min(this.id), _longest: max(this.id) } AS var0 - } + "CYPHER 5 CALL { MATCH (this:Film) WITH this ORDER BY size(this.title) DESC WITH collect(this.title) AS list - RETURN { _longest: head(list), _shortest: last(list) } AS var1 + RETURN { _longest: head(list), _shortest: last(list) } AS var0 } CALL { MATCH (this:Film) - RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var2 + RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var1 } CALL { MATCH (this:Film) - RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var3 + RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var2 } - RETURN { _id: var0, _title: var1, _imdbRating: var2, _createdAt: var3 }" + RETURN { _title: var0, _imdbRating: var1, _createdAt: var2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); @@ -103,10 +96,6 @@ describe("Cypher Aggregations Many while Alias fields", () => { const query = /* GraphQL */ ` { actorsAggregate { - _id: id { - _shortest: shortest - _longest: longest - } _name: name { _shortest: shortest _longest: longest @@ -127,26 +116,23 @@ describe("Cypher Aggregations Many while Alias fields", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Actor:Person:Alien) - RETURN { _shortest: min(this.id), _longest: max(this.id) } AS var0 - } + "CYPHER 5 CALL { MATCH (this:Actor:Person:Alien) WITH this ORDER BY size(this.name) DESC WITH collect(this.name) AS list - RETURN { _longest: head(list), _shortest: last(list) } AS var1 + RETURN { _longest: head(list), _shortest: last(list) } AS var0 } CALL { MATCH (this:Actor:Person:Alien) - RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var2 + RETURN { _min: min(this.imdbRating), _max: max(this.imdbRating), _average: avg(this.imdbRating) } AS var1 } CALL { MATCH (this:Actor:Person:Alien) - RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var3 + RETURN { _min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), _max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var2 } - RETURN { _id: var0, _name: var1, _imdbRating: var2, _createdAt: var3 }" + RETURN { _name: var0, _imdbRating: var1, _createdAt: var2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/aggregations/localdatetime.test.ts b/packages/graphql/tests/tck/aggregations/localdatetime.test.ts index ad8ef8532b..f4599712e0 100644 --- a/packages/graphql/tests/tck/aggregations/localdatetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/localdatetime.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.createdAt) } AS var0 } @@ -99,7 +101,8 @@ describe("Cypher Aggregations LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt), max: max(this.createdAt) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/localtime.test.ts b/packages/graphql/tests/tck/aggregations/localtime.test.ts index 689ac314db..e1f8113ace 100644 --- a/packages/graphql/tests/tck/aggregations/localtime.test.ts +++ b/packages/graphql/tests/tck/aggregations/localtime.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.createdAt) } AS var0 } @@ -99,7 +101,8 @@ describe("Cypher Aggregations LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt), max: max(this.createdAt) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/many.test.ts b/packages/graphql/tests/tck/aggregations/many.test.ts index 079a9bd26f..ddfa385007 100644 --- a/packages/graphql/tests/tck/aggregations/many.test.ts +++ b/packages/graphql/tests/tck/aggregations/many.test.ts @@ -43,10 +43,6 @@ describe("Cypher Aggregations Many", () => { const query = /* GraphQL */ ` { moviesAggregate { - id { - shortest - longest - } title { shortest longest @@ -67,26 +63,23 @@ describe("Cypher Aggregations Many", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (this:Movie) - RETURN { shortest: min(this.id), longest: max(this.id) } AS var0 - } + "CYPHER 5 CALL { MATCH (this:Movie) WITH this ORDER BY size(this.title) DESC WITH collect(this.title) AS list - RETURN { longest: head(list), shortest: last(list) } AS var1 + RETURN { longest: head(list), shortest: last(list) } AS var0 } CALL { MATCH (this:Movie) - RETURN { min: min(this.imdbRating), max: max(this.imdbRating), average: avg(this.imdbRating) } AS var2 + RETURN { min: min(this.imdbRating), max: max(this.imdbRating), average: avg(this.imdbRating) } AS var1 } CALL { MATCH (this:Movie) - RETURN { min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var3 + RETURN { min: apoc.date.convertFormat(toString(min(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), max: apoc.date.convertFormat(toString(max(this.createdAt)), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS var2 } - RETURN { id: var0, title: var1, imdbRating: var2, createdAt: var3 }" + RETURN { title: var0, imdbRating: var1, createdAt: var2 }" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); diff --git a/packages/graphql/tests/tck/aggregations/string.test.ts b/packages/graphql/tests/tck/aggregations/string.test.ts index c5569be1cc..8c93821e75 100644 --- a/packages/graphql/tests/tck/aggregations/string.test.ts +++ b/packages/graphql/tests/tck/aggregations/string.test.ts @@ -51,7 +51,8 @@ describe("Cypher Aggregations String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) WITH this ORDER BY size(this.title) DESC @@ -78,7 +79,8 @@ describe("Cypher Aggregations String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) WITH this ORDER BY size(this.title) DESC @@ -106,7 +108,8 @@ describe("Cypher Aggregations String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) WITH this ORDER BY size(this.title) DESC @@ -122,7 +125,7 @@ describe("Cypher Aggregations String", () => { test("Shortest with filter", async () => { const query = /* GraphQL */ ` { - moviesAggregate(where: { testId_EQ: "10" }) { + moviesAggregate(where: { testId: { eq: "10" } }) { title { shortest } @@ -133,7 +136,8 @@ describe("Cypher Aggregations String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) WHERE this.testId = $param0 WITH this diff --git a/packages/graphql/tests/tck/aggregations/time.test.ts b/packages/graphql/tests/tck/aggregations/time.test.ts index 9b333a84de..51efeb0ecb 100644 --- a/packages/graphql/tests/tck/aggregations/time.test.ts +++ b/packages/graphql/tests/tck/aggregations/time.test.ts @@ -50,7 +50,8 @@ describe("Cypher Aggregations Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt) } AS var0 } @@ -74,7 +75,8 @@ describe("Cypher Aggregations Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { max: max(this.createdAt) } AS var0 } @@ -99,7 +101,8 @@ describe("Cypher Aggregations Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) RETURN { min: min(this.createdAt), max: max(this.createdAt) } AS var0 } diff --git a/packages/graphql/tests/tck/aggregations/where/count.test.ts b/packages/graphql/tests/tck/aggregations/where/count.test.ts index b3b4805a5e..ab09e8c45e 100644 --- a/packages/graphql/tests/tck/aggregations/where/count.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/count.test.ts @@ -44,7 +44,7 @@ describe("Cypher Aggregations where with count", () => { test("Equality Count", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_EQ: 10 } }) { + posts(where: { likesAggregate: { count: { eq: 10 } } }) { content } } @@ -53,7 +53,8 @@ describe("Cypher Aggregations where with count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -77,7 +78,7 @@ describe("Cypher Aggregations where with count", () => { test("LT Count", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_LT: 10 } }) { + posts(where: { likesAggregate: { count: { lt: 10 } } }) { content } } @@ -86,7 +87,8 @@ describe("Cypher Aggregations where with count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -110,7 +112,7 @@ describe("Cypher Aggregations where with count", () => { test("LTE Count", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_LTE: 10 } }) { + posts(where: { likesAggregate: { count: { lte: 10 } } }) { content } } @@ -119,7 +121,8 @@ describe("Cypher Aggregations where with count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -143,7 +146,7 @@ describe("Cypher Aggregations where with count", () => { test("GT Count", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_GT: 10 } }) { + posts(where: { likesAggregate: { count: { gt: 10 } } }) { content } } @@ -152,7 +155,8 @@ describe("Cypher Aggregations where with count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -176,7 +180,7 @@ describe("Cypher Aggregations where with count", () => { test("GTE Count", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_GTE: 10 } }) { + posts(where: { likesAggregate: { count: { gte: 10 } } }) { content } } @@ -185,7 +189,8 @@ describe("Cypher Aggregations where with count", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/bigint.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/bigint.test.ts index 8ef9e8f4af..d3d6a8286d 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/bigint.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/bigint.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_AVERAGE_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { average: { eq: "2147483648" } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -82,7 +83,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_AVERAGE_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { average: { gt: "2147483648" } } } } }) { content } } @@ -91,7 +92,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -115,7 +117,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_AVERAGE_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { average: { gte: "2147483648" } } } } }) { content } } @@ -124,7 +126,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -148,7 +151,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_AVERAGE_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { average: { lt: "2147483648" } } } } }) { content } } @@ -157,7 +160,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -181,7 +185,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_AVERAGE_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { average: { lte: "2147483648" } } } } }) { content } } @@ -190,7 +194,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -214,7 +219,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_SUM_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { sum: { eq: "2147483648" } } } } }) { content } } @@ -223,7 +228,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -247,7 +253,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_SUM_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { sum: { gt: "2147483648" } } } } }) { content } } @@ -256,7 +262,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -280,7 +287,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_SUM_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { sum: { gte: "2147483648" } } } } }) { content } } @@ -289,7 +296,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -313,7 +321,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_SUM_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { sum: { lt: "2147483648" } } } } }) { content } } @@ -322,7 +330,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -346,7 +355,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_SUM_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { sum: { lte: "2147483648" } } } } }) { content } } @@ -355,7 +364,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -379,7 +389,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MIN_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { min: { eq: "2147483648" } } } } }) { content } } @@ -388,7 +398,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -412,7 +423,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MIN_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { min: { gt: "2147483648" } } } } }) { content } } @@ -421,7 +432,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -445,7 +457,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MIN_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { min: { gte: "2147483648" } } } } }) { content } } @@ -454,7 +466,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -478,7 +491,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MIN_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { min: { lt: "2147483648" } } } } }) { content } } @@ -487,7 +500,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -511,7 +525,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MIN_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { min: { lte: "2147483648" } } } } }) { content } } @@ -520,7 +534,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -544,7 +559,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MAX_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { max: { eq: "2147483648" } } } } }) { content } } @@ -553,7 +568,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -577,7 +593,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MAX_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { max: { gt: "2147483648" } } } } }) { content } } @@ -586,7 +602,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -610,7 +627,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MAX_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { max: { gte: "2147483648" } } } } }) { content } } @@ -619,7 +636,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -643,7 +661,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MAX_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { max: { lt: "2147483648" } } } } }) { content } } @@ -652,7 +670,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -676,7 +695,7 @@ describe("Cypher Aggregations where edge with BigInt", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someBigInt_MAX_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { edge: { someBigInt: { max: { lte: "2147483648" } } } } }) { content } } @@ -685,7 +704,8 @@ describe("Cypher Aggregations where edge with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts index cd9028effa..b272aad9c3 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts @@ -49,7 +49,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MIN_EQUAL: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { min: { eq: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -58,11 +60,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) = $param0 AS var2 + RETURN min(this0.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -71,16 +74,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -88,7 +82,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MIN_GT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { min: { gt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -97,11 +93,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) > $param0 AS var2 + RETURN min(this0.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -110,16 +107,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -127,7 +115,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MIN_GTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { min: { gte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -136,11 +126,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) >= $param0 AS var2 + RETURN min(this0.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -149,16 +140,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -166,7 +148,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MIN_LT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { min: { lt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -175,11 +159,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) < $param0 AS var2 + RETURN min(this0.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -188,16 +173,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -205,7 +181,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MIN_LTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { min: { lte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -214,11 +192,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) <= $param0 AS var2 + RETURN min(this0.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -227,16 +206,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -244,7 +214,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MAX_EQUAL: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { max: { eq: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -253,11 +225,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) = $param0 AS var2 + RETURN max(this0.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -266,16 +239,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -283,7 +247,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MAX_GT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { max: { gt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -292,11 +258,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) > $param0 AS var2 + RETURN max(this0.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -305,16 +272,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -322,7 +280,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MAX_GTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { max: { gte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -331,11 +291,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) >= $param0 AS var2 + RETURN max(this0.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -344,16 +305,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -361,7 +313,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MAX_LT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { max: { lt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -370,11 +324,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) < $param0 AS var2 + RETURN max(this0.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -383,16 +338,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -400,7 +346,9 @@ describe("Cypher Aggregations where edge with DateTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDateTime_MAX_LTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { edge: { someDateTime: { max: { lte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -409,11 +357,12 @@ describe("Cypher Aggregations where edge with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) <= $param0 AS var2 + RETURN max(this0.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -422,16 +371,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/edge/duration.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/duration.test.ts index e1439f2146..3971db945c 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/duration.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/duration.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_AVERAGE_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { average: { eq: "P1Y" } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -90,7 +91,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_AVERAGE_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { average: { gt: "P1Y" } } } } }) { content } } @@ -99,7 +100,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -131,7 +133,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_AVERAGE_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { average: { gte: "P1Y" } } } } }) { content } } @@ -140,7 +142,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -172,7 +175,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_AVERAGE_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { average: { lt: "P1Y" } } } } }) { content } } @@ -181,7 +184,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -213,7 +217,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_AVERAGE_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { average: { lte: "P1Y" } } } } }) { content } } @@ -222,7 +226,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -254,7 +259,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MIN_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { min: { eq: "P1Y" } } } } }) { content } } @@ -263,7 +268,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -295,7 +301,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MIN_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { min: { gt: "P1Y" } } } } }) { content } } @@ -304,7 +310,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -336,7 +343,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MIN_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { min: { gte: "P1Y" } } } } }) { content } } @@ -345,7 +352,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -377,7 +385,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MIN_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { min: { lt: "P1Y" } } } } }) { content } } @@ -386,7 +394,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -418,7 +427,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MIN_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { min: { lte: "P1Y" } } } } }) { content } } @@ -427,7 +436,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -459,7 +469,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MAX_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { max: { eq: "P1Y" } } } } }) { content } } @@ -468,7 +478,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -500,7 +511,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MAX_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { max: { gt: "P1Y" } } } } }) { content } } @@ -509,7 +520,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -541,7 +553,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MAX_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { max: { gte: "P1Y" } } } } }) { content } } @@ -550,7 +562,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -582,7 +595,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MAX_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { max: { lt: "P1Y" } } } } }) { content } } @@ -591,7 +604,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -623,7 +637,7 @@ describe("Cypher Aggregations where edge with Duration", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someDuration_MAX_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { edge: { someDuration: { max: { lte: "P1Y" } } } } }) { content } } @@ -632,7 +646,8 @@ describe("Cypher Aggregations where edge with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/float.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/float.test.ts index 0dcd709067..fdcfef8e8c 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/float.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/float.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_AVERAGE_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { average: { eq: 10 } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -79,7 +80,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_AVERAGE_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { average: { gt: 10 } } } } }) { content } } @@ -88,7 +89,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -109,7 +111,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_AVERAGE_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { average: { gte: 10 } } } } }) { content } } @@ -118,7 +120,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -139,7 +142,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_AVERAGE_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { average: { lt: 10 } } } } }) { content } } @@ -148,7 +151,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -169,7 +173,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_AVERAGE_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { average: { lte: 10 } } } } }) { content } } @@ -178,7 +182,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -199,7 +204,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_SUM_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { sum: { eq: 10 } } } } }) { content } } @@ -208,7 +213,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -229,7 +235,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_SUM_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { sum: { gt: 10 } } } } }) { content } } @@ -238,7 +244,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -259,7 +266,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_SUM_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { sum: { gte: 10 } } } } }) { content } } @@ -268,7 +275,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -289,7 +297,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_SUM_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { sum: { lt: 10 } } } } }) { content } } @@ -298,7 +306,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -319,7 +328,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_SUM_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { sum: { lte: 10 } } } } }) { content } } @@ -328,7 +337,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -349,7 +359,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MIN_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { min: { eq: 10 } } } } }) { content } } @@ -358,7 +368,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -379,7 +390,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MIN_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { min: { gt: 10 } } } } }) { content } } @@ -388,7 +399,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -409,7 +421,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MIN_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { min: { gte: 10 } } } } }) { content } } @@ -418,7 +430,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -439,7 +452,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MIN_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { min: { lt: 10 } } } } }) { content } } @@ -448,7 +461,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -469,7 +483,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MIN_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { min: { lte: 10 } } } } }) { content } } @@ -478,7 +492,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -499,7 +514,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MAX_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { max: { eq: 10 } } } } }) { content } } @@ -508,7 +523,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -529,7 +545,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MAX_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { max: { gt: 10 } } } } }) { content } } @@ -538,7 +554,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -559,7 +576,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MAX_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { max: { gte: 10 } } } } }) { content } } @@ -568,7 +585,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -589,7 +607,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MAX_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { max: { lt: 10 } } } } }) { content } } @@ -598,7 +616,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -619,7 +638,7 @@ describe("Cypher Aggregations where edge with Float", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someFloat_MAX_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someFloat: { max: { lte: 10 } } } } }) { content } } @@ -628,7 +647,8 @@ describe("Cypher Aggregations where edge with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/int.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/int.test.ts index d673c73581..c90fa3a7a7 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/int.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/int.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_AVERAGE_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { average: { eq: 10 } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -79,7 +80,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_AVERAGE_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { average: { gt: 10 } } } } }) { content } } @@ -88,7 +89,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -109,7 +111,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_AVERAGE_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { average: { gte: 10 } } } } }) { content } } @@ -118,7 +120,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -139,7 +142,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_AVERAGE_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { average: { lt: 10 } } } } }) { content } } @@ -148,7 +151,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -169,7 +173,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_AVERAGE_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { average: { lte: 10 } } } } }) { content } } @@ -178,7 +182,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -199,7 +204,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_SUM_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { sum: { eq: 10 } } } } }) { content } } @@ -208,7 +213,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -232,7 +238,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_SUM_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { sum: { gt: 10 } } } } }) { content } } @@ -241,7 +247,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -265,7 +272,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_SUM_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { sum: { gte: 10 } } } } }) { content } } @@ -274,7 +281,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -298,7 +306,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_SUM_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { sum: { lt: 10 } } } } }) { content } } @@ -307,7 +315,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -331,7 +340,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_SUM_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { sum: { lte: 10 } } } } }) { content } } @@ -340,7 +349,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -364,7 +374,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MIN_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { min: { eq: 10 } } } } }) { content } } @@ -373,7 +383,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -397,7 +408,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MIN_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { min: { gt: 10 } } } } }) { content } } @@ -406,7 +417,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -430,7 +442,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MIN_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { min: { gte: 10 } } } } }) { content } } @@ -439,7 +451,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -463,7 +476,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MIN_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { min: { lt: 10 } } } } }) { content } } @@ -472,7 +485,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -496,7 +510,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MIN_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { min: { lte: 10 } } } } }) { content } } @@ -505,7 +519,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -529,7 +544,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MAX_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { max: { eq: 10 } } } } }) { content } } @@ -538,7 +553,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -562,7 +578,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MAX_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { max: { gt: 10 } } } } }) { content } } @@ -571,7 +587,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -595,7 +612,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MAX_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { max: { gte: 10 } } } } }) { content } } @@ -604,7 +621,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -628,7 +646,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MAX_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { max: { lt: 10 } } } } }) { content } } @@ -637,7 +655,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -661,7 +680,7 @@ describe("Cypher Aggregations where edge with Int", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someInt_MAX_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someInt: { max: { lte: 10 } } } } }) { content } } @@ -670,7 +689,8 @@ describe("Cypher Aggregations where edge with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/interface-relationship.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/interface-relationship.test.ts index a1adb49279..cea0106de0 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/interface-relationship.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/interface-relationship.test.ts @@ -79,7 +79,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1) @@ -119,7 +120,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:Actor) CALL { WITH this0 @@ -173,7 +175,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:Actor) CALL { WITH this0 diff --git a/packages/graphql/tests/tck/aggregations/where/edge/localdatetime.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/localdatetime.test.ts index 629c961f03..1ff2276856 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/localdatetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/localdatetime.test.ts @@ -49,7 +49,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MIN_EQUAL: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { min: { eq: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -58,7 +60,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -87,7 +90,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MIN_GT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { min: { gt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -96,7 +101,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -125,7 +131,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MIN_GTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { min: { gte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -134,7 +142,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -163,7 +172,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MIN_LT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { min: { lt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -172,7 +183,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -201,7 +213,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MIN_LTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { min: { lte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -210,7 +224,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -239,7 +254,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MAX_EQUAL: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { max: { eq: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -248,7 +265,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -277,7 +295,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MAX_GT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { max: { gt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -286,7 +306,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -315,7 +336,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MAX_GTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { max: { gte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -324,7 +347,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -353,7 +377,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MAX_LT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { max: { lt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -362,7 +388,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -391,7 +418,9 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalDateTime_MAX_LTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { edge: { someLocalDateTime: { max: { lte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -400,7 +429,8 @@ describe("Cypher Aggregations where edge with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/localtime.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/localtime.test.ts index c4b4faef5f..68ff7561d7 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/localtime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/localtime.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MIN_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { min: { eq: "12:00:00" } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -84,7 +85,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MIN_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { min: { gt: "12:00:00" } } } } }) { content } } @@ -93,7 +94,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -119,7 +121,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MIN_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { min: { gte: "12:00:00" } } } } }) { content } } @@ -128,7 +130,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -154,7 +157,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MIN_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { min: { lt: "12:00:00" } } } } }) { content } } @@ -163,7 +166,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -189,7 +193,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MIN_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { min: { lte: "12:00:00" } } } } }) { content } } @@ -198,7 +202,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -224,7 +229,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MAX_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { max: { eq: "12:00:00" } } } } }) { content } } @@ -233,7 +238,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -259,7 +265,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MAX_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { max: { gt: "12:00:00" } } } } }) { content } } @@ -268,7 +274,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -294,7 +301,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MAX_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { max: { gte: "12:00:00" } } } } }) { content } } @@ -303,7 +310,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -329,7 +337,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MAX_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { max: { lt: "12:00:00" } } } } }) { content } } @@ -338,7 +346,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -364,7 +373,7 @@ describe("Cypher Aggregations where edge with LocalTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someLocalTime_MAX_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someLocalTime: { max: { lte: "12:00:00" } } } } }) { content } } @@ -373,7 +382,8 @@ describe("Cypher Aggregations where edge with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/string.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/string.test.ts index 809c5337ca..241cfc90bc 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/string.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/string.test.ts @@ -46,10 +46,10 @@ describe("Cypher Aggregations where edge with String", () => { }); }); - test("SHORTEST_LENGTH_EQUAL", async () => { + test("shortestLength_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_SHORTEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { shortestLength: { eq: 10 } } } } }) { content } } @@ -58,7 +58,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -79,10 +80,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("SHORTEST_LENGTH_GT", async () => { + test("shortestLength_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_SHORTEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { shortestLength: { gt: 10 } } } } }) { content } } @@ -91,7 +92,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -112,10 +114,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("SHORTEST_LENGTH_GTE", async () => { + test("shortestLength_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_SHORTEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { shortestLength: { gte: 10 } } } } }) { content } } @@ -124,7 +126,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -145,10 +148,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("SHORTEST_LENGTH_LT", async () => { + test("shortestLength_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_SHORTEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { shortestLength: { lt: 10 } } } } }) { content } } @@ -157,7 +160,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -178,10 +182,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("SHORTEST_LENGTH_LTE", async () => { + test("shortestLength_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_SHORTEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { shortestLength: { lte: 10 } } } } }) { content } } @@ -190,7 +194,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -211,10 +216,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("LONGEST_LENGTH_EQUAL", async () => { + test("longestLength_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_LONGEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { longestLength: { eq: 10 } } } } }) { content } } @@ -223,7 +228,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -244,10 +250,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("LONGEST_LENGTH_GT", async () => { + test("longestLength_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_LONGEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { longestLength: { gt: 10 } } } } }) { content } } @@ -256,7 +262,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -277,10 +284,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("LONGEST_LENGTH_GTE", async () => { + test("longestLength_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_LONGEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { longestLength: { gte: 10 } } } } }) { content } } @@ -289,7 +296,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -310,10 +318,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("LONGEST_LENGTH_LT", async () => { + test("longestLength_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_LONGEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { longestLength: { lt: 10 } } } } }) { content } } @@ -322,7 +330,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -343,10 +352,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("LONGEST_LENGTH_LTE", async () => { + test("longestLength_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_LONGEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { longestLength: { lte: 10 } } } } }) { content } } @@ -355,7 +364,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -376,10 +386,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("AVERAGE_LENGTH_EQUAL", async () => { + test("averageLength_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_AVERAGE_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { averageLength: { eq: 10 } } } } }) { content } } @@ -388,7 +398,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -406,10 +417,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("AVERAGE_LENGTH_GT", async () => { + test("averageLength_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_AVERAGE_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { averageLength: { gt: 10 } } } } }) { content } } @@ -418,7 +429,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -436,10 +448,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("AVERAGE_LENGTH_GTE", async () => { + test("averageLength_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_AVERAGE_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { averageLength: { gte: 10 } } } } }) { content } } @@ -448,7 +460,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -466,10 +479,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("AVERAGE_LENGTH_LT", async () => { + test("averageLength_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_AVERAGE_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { averageLength: { lt: 10 } } } } }) { content } } @@ -478,7 +491,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -496,10 +510,10 @@ describe("Cypher Aggregations where edge with String", () => { `); }); - test("AVERAGE_LENGTH_LTE", async () => { + test("averageLength_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someString_AVERAGE_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { edge: { someString: { averageLength: { lte: 10 } } } } }) { content } } @@ -508,7 +522,8 @@ describe("Cypher Aggregations where edge with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts index c70659f3ea..6314488e88 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts @@ -49,7 +49,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MIN_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { min: { eq: "12:00:00" } } } } }) { content } } @@ -58,11 +58,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) = $param0 AS var2 + RETURN min(this0.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -71,13 +72,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -85,7 +80,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MIN_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { min: { gt: "12:00:00" } } } } }) { content } } @@ -94,11 +89,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) > $param0 AS var2 + RETURN min(this0.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -107,13 +103,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -121,7 +111,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MIN_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { min: { gte: "12:00:00" } } } } }) { content } } @@ -130,11 +120,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) >= $param0 AS var2 + RETURN min(this0.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -143,13 +134,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -157,7 +142,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MIN_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { min: { lt: "12:00:00" } } } } }) { content } } @@ -166,11 +151,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) < $param0 AS var2 + RETURN min(this0.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -179,13 +165,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -193,7 +173,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MIN_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { min: { lte: "12:00:00" } } } } }) { content } } @@ -202,11 +182,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) <= $param0 AS var2 + RETURN min(this0.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -215,13 +196,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -229,7 +204,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MAX_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { max: { eq: "12:00:00" } } } } }) { content } } @@ -238,11 +213,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) = $param0 AS var2 + RETURN max(this0.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -251,13 +227,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -265,7 +235,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MAX_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { max: { gt: "12:00:00" } } } } }) { content } } @@ -274,11 +244,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) > $param0 AS var2 + RETURN max(this0.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -287,13 +258,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -301,7 +266,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MAX_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { max: { gte: "12:00:00" } } } } }) { content } } @@ -310,11 +275,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) >= $param0 AS var2 + RETURN max(this0.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -323,13 +289,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -337,7 +297,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MAX_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { max: { lt: "12:00:00" } } } } }) { content } } @@ -346,11 +306,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) < $param0 AS var2 + RETURN max(this0.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -359,13 +320,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -373,7 +328,7 @@ describe("Cypher Aggregations where edge with Time", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { edge: { someTime_MAX_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { edge: { someTime: { max: { lte: "12:00:00" } } } } }) { content } } @@ -382,11 +337,12 @@ describe("Cypher Aggregations where edge with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) <= $param0 AS var2 + RETURN max(this0.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -395,13 +351,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/logical.test.ts b/packages/graphql/tests/tck/aggregations/where/logical.test.ts index a332457b2d..b4a82b35c3 100644 --- a/packages/graphql/tests/tck/aggregations/where/logical.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/logical.test.ts @@ -44,7 +44,7 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { test("AND", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { AND: [{ count_GT: 10 }, { count_LT: 20 }] } }) { + posts(where: { likesAggregate: { AND: [{ count: { gt: 10 } }, { count: { lt: 20 } }] } }) { content } } @@ -53,7 +53,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -81,7 +82,7 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { test("OR", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { OR: [{ count_GT: 10 }, { count_LT: 20 }] } }) { + posts(where: { likesAggregate: { OR: [{ count: { gt: 10 } }, { count: { lt: 20 } }] } }) { content } } @@ -90,7 +91,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -118,7 +120,7 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { test("NOT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { NOT: { count_GT: 10 } } }) { + posts(where: { likesAggregate: { NOT: { count: { gt: 10 } } } }) { content } } @@ -127,7 +129,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -154,8 +157,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { posts( where: { likesAggregate: { - AND: [{ count_GT: 10 }, { count_LT: 20 }] - OR: [{ count_GT: 10 }, { count_LT: 20 }] + AND: [{ count: { gt: 10 } }, { count: { lt: 20 } }] + OR: [{ count: { gt: 10 } }, { count: { lt: 20 } }] } } ) { @@ -167,7 +170,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -206,9 +210,8 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { posts( where: { likesAggregate: { - count_GT: 10 - count_LT: 20 - OR: [{ count_GT: 10 }, { count_LT: 20 }, { count_LT: 54 }] + count: { gt: 10, lt: 20 } + OR: [{ count: { gt: 10 } }, { count: { lt: 20 } }, { count: { lt: 54 } }] } } ) { @@ -220,11 +223,12 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) < $param0 AND count(this1) > $param1 AND (count(this1) > $param2 OR count(this1) < $param3 OR count(this1) < $param4)) AS var2 + RETURN (count(this1) > $param0 AND count(this1) < $param1 AND (count(this1) > $param2 OR count(this1) < $param3 OR count(this1) < $param4)) AS var2 } WITH * WHERE var2 = true @@ -234,11 +238,11 @@ describe("Cypher Aggregations where with logical AND plus OR", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { - \\"low\\": 20, + \\"low\\": 10, \\"high\\": 0 }, \\"param1\\": { - \\"low\\": 10, + \\"low\\": 20, \\"high\\": 0 }, \\"param2\\": { diff --git a/packages/graphql/tests/tck/aggregations/where/node/bigint.test.ts b/packages/graphql/tests/tck/aggregations/where/node/bigint.test.ts index 6e0aca951a..7e0c773bb8 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/bigint.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/bigint.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_AVERAGE_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { average: { eq: "2147483648" } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -78,7 +79,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_AVERAGE_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { average: { gt: "2147483648" } } } } }) { content } } @@ -87,7 +88,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -111,7 +113,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_AVERAGE_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { average: { gte: "2147483648" } } } } }) { content } } @@ -120,7 +122,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -144,7 +147,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_AVERAGE_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { average: { lt: "2147483648" } } } } }) { content } } @@ -153,7 +156,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -177,7 +181,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_AVERAGE_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { average: { lte: "2147483648" } } } } }) { content } } @@ -186,7 +190,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -210,7 +215,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_SUM_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { sum: { eq: "2147483648" } } } } }) { content } } @@ -219,7 +224,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -243,7 +249,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_SUM_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { sum: { gt: "2147483648" } } } } }) { content } } @@ -252,7 +258,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -276,7 +283,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_SUM_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { sum: { gte: "2147483648" } } } } }) { content } } @@ -285,7 +292,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -309,7 +317,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_SUM_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { sum: { lt: "2147483648" } } } } }) { content } } @@ -318,7 +326,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -342,7 +351,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_SUM_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { sum: { lte: "2147483648" } } } } }) { content } } @@ -351,7 +360,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -375,7 +385,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MIN_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { min: { eq: "2147483648" } } } } }) { content } } @@ -384,7 +394,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -408,7 +419,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MIN_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { min: { gt: "2147483648" } } } } }) { content } } @@ -417,7 +428,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -441,7 +453,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MIN_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { min: { gte: "2147483648" } } } } }) { content } } @@ -450,7 +462,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -474,7 +487,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MIN_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { min: { lt: "2147483648" } } } } }) { content } } @@ -483,7 +496,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -507,7 +521,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MIN_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { min: { lte: "2147483648" } } } } }) { content } } @@ -516,7 +530,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -540,7 +555,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MAX_EQUAL: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { max: { eq: "2147483648" } } } } }) { content } } @@ -549,7 +564,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -573,7 +589,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MAX_GT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { max: { gt: "2147483648" } } } } }) { content } } @@ -582,7 +598,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -606,7 +623,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MAX_GTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { max: { gte: "2147483648" } } } } }) { content } } @@ -615,7 +632,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -639,7 +657,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MAX_LT: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { max: { lt: "2147483648" } } } } }) { content } } @@ -648,7 +666,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -672,7 +691,7 @@ describe("Cypher Aggregations where node with BigInt", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someBigInt_MAX_LTE: "2147483648" } } }) { + posts(where: { likesAggregate: { node: { someBigInt: { max: { lte: "2147483648" } } } } }) { content } } @@ -681,7 +700,8 @@ describe("Cypher Aggregations where node with BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts b/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts index 68c3553c38..80acc84aa7 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts @@ -45,7 +45,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MIN_EQUAL: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { min: { eq: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -54,11 +56,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) = $param0 AS var2 + RETURN min(this1.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -67,16 +70,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -84,7 +78,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MIN_GT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { min: { gt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -93,11 +89,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) > $param0 AS var2 + RETURN min(this1.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -106,16 +103,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -123,7 +111,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MIN_GTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { min: { gte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -132,11 +122,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) >= $param0 AS var2 + RETURN min(this1.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -145,16 +136,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -162,7 +144,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MIN_LT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { min: { lt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -171,11 +155,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) < $param0 AS var2 + RETURN min(this1.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -184,16 +169,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -201,7 +177,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MIN_LTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { min: { lte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -210,11 +188,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) <= $param0 AS var2 + RETURN min(this1.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -223,16 +202,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -240,7 +210,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MAX_EQUAL: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { max: { eq: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -249,11 +221,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) = $param0 AS var2 + RETURN max(this1.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -262,16 +235,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -279,7 +243,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MAX_GT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { max: { gt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -288,11 +254,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) > $param0 AS var2 + RETURN max(this1.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -301,16 +268,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -318,7 +276,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MAX_GTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { max: { gte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -327,11 +287,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) >= $param0 AS var2 + RETURN max(this1.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -340,16 +301,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -357,7 +309,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MAX_LT: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { max: { lt: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -366,11 +320,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) < $param0 AS var2 + RETURN max(this1.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -379,16 +334,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -396,7 +342,9 @@ describe("Cypher Aggregations where node with DateTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDateTime_MAX_LTE: "2021-09-25T12:51:24.037Z" } } }) { + posts( + where: { likesAggregate: { node: { someDateTime: { max: { lte: "2021-09-25T12:51:24.037Z" } } } } } + ) { content } } @@ -405,11 +353,12 @@ describe("Cypher Aggregations where node with DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) <= $param0 AS var2 + RETURN max(this1.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -418,16 +367,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/node/duration.test.ts b/packages/graphql/tests/tck/aggregations/where/node/duration.test.ts index d545430191..bec8fb66e8 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/duration.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/duration.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_AVERAGE_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { average: { eq: "P1Y" } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -86,7 +87,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_AVERAGE_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { average: { gt: "P1Y" } } } } }) { content } } @@ -95,7 +96,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -127,7 +129,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_AVERAGE_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { average: { gte: "P1Y" } } } } }) { content } } @@ -136,7 +138,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -168,7 +171,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_AVERAGE_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { average: { lt: "P1Y" } } } } }) { content } } @@ -177,7 +180,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -209,7 +213,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_AVERAGE_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { average: { lte: "P1Y" } } } } }) { content } } @@ -218,7 +222,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -250,7 +255,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MIN_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { min: { eq: "P1Y" } } } } }) { content } } @@ -259,7 +264,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -291,7 +297,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MIN_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { min: { gt: "P1Y" } } } } }) { content } } @@ -300,7 +306,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -332,7 +339,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MIN_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { min: { gte: "P1Y" } } } } }) { content } } @@ -341,7 +348,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -373,7 +381,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MIN_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { min: { lt: "P1Y" } } } } }) { content } } @@ -382,7 +390,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -414,7 +423,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MIN_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { min: { lte: "P1Y" } } } } }) { content } } @@ -423,7 +432,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -455,7 +465,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MAX_EQUAL: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { max: { eq: "P1Y" } } } } }) { content } } @@ -464,7 +474,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -496,7 +507,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MAX_GT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { max: { gt: "P1Y" } } } } }) { content } } @@ -505,7 +516,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -537,7 +549,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MAX_GTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { max: { gte: "P1Y" } } } } }) { content } } @@ -546,7 +558,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -578,7 +591,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MAX_LT: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { max: { lt: "P1Y" } } } } }) { content } } @@ -587,7 +600,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -619,7 +633,7 @@ describe("Cypher Aggregations where node with Duration", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someDuration_MAX_LTE: "P1Y" } } }) { + posts(where: { likesAggregate: { node: { someDuration: { max: { lte: "P1Y" } } } } }) { content } } @@ -628,7 +642,8 @@ describe("Cypher Aggregations where node with Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/float.test.ts b/packages/graphql/tests/tck/aggregations/where/node/float.test.ts index 9ddedb59b7..61fc49dc78 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/float.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/float.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with Float", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_AVERAGE_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { average: { eq: 10 } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -75,7 +76,7 @@ describe("Cypher Aggregations where node with Float", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_AVERAGE_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { average: { gt: 10 } } } } }) { content } } @@ -84,7 +85,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -105,7 +107,7 @@ describe("Cypher Aggregations where node with Float", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_AVERAGE_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { average: { gte: 10 } } } } }) { content } } @@ -114,7 +116,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -135,7 +138,7 @@ describe("Cypher Aggregations where node with Float", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_AVERAGE_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { average: { lt: 10 } } } } }) { content } } @@ -144,7 +147,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -165,7 +169,7 @@ describe("Cypher Aggregations where node with Float", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_AVERAGE_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { average: { lte: 10 } } } } }) { content } } @@ -174,7 +178,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -195,7 +200,7 @@ describe("Cypher Aggregations where node with Float", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_SUM_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { sum: { eq: 10 } } } } }) { content } } @@ -204,7 +209,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -225,7 +231,7 @@ describe("Cypher Aggregations where node with Float", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_SUM_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { sum: { gt: 10 } } } } }) { content } } @@ -234,7 +240,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -255,7 +262,7 @@ describe("Cypher Aggregations where node with Float", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_SUM_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { sum: { gte: 10 } } } } }) { content } } @@ -264,7 +271,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -285,7 +293,7 @@ describe("Cypher Aggregations where node with Float", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_SUM_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { sum: { lt: 10 } } } } }) { content } } @@ -294,7 +302,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -315,7 +324,7 @@ describe("Cypher Aggregations where node with Float", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_SUM_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { sum: { lte: 10 } } } } }) { content } } @@ -324,7 +333,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -345,7 +355,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MIN_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { min: { eq: 10 } } } } }) { content } } @@ -354,7 +364,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -375,7 +386,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MIN_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { min: { gt: 10 } } } } }) { content } } @@ -384,7 +395,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -405,7 +417,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MIN_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { min: { gte: 10 } } } } }) { content } } @@ -414,7 +426,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -435,7 +448,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MIN_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { min: { lt: 10 } } } } }) { content } } @@ -444,7 +457,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -465,7 +479,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MIN_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { min: { lte: 10 } } } } }) { content } } @@ -474,7 +488,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -495,7 +510,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MAX_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { max: { eq: 10 } } } } }) { content } } @@ -504,7 +519,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -525,7 +541,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MAX_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { max: { gt: 10 } } } } }) { content } } @@ -534,7 +550,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -555,7 +572,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MAX_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { max: { gte: 10 } } } } }) { content } } @@ -564,7 +581,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -585,7 +603,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MAX_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { max: { lt: 10 } } } } }) { content } } @@ -594,7 +612,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -615,7 +634,7 @@ describe("Cypher Aggregations where node with Float", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someFloat_MAX_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someFloat: { max: { lte: 10 } } } } }) { content } } @@ -624,7 +643,8 @@ describe("Cypher Aggregations where node with Float", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/int.test.ts b/packages/graphql/tests/tck/aggregations/where/node/int.test.ts index 5b1b3fe802..4a1c88d717 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/int.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/int.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with Int", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_AVERAGE_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { average: { eq: 10 } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -75,7 +76,7 @@ describe("Cypher Aggregations where node with Int", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_AVERAGE_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { average: { gt: 10 } } } } }) { content } } @@ -84,7 +85,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -105,7 +107,7 @@ describe("Cypher Aggregations where node with Int", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_AVERAGE_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { average: { gte: 10 } } } } }) { content } } @@ -114,7 +116,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -135,7 +138,7 @@ describe("Cypher Aggregations where node with Int", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_AVERAGE_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { average: { lt: 10 } } } } }) { content } } @@ -144,7 +147,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -165,7 +169,7 @@ describe("Cypher Aggregations where node with Int", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_AVERAGE_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { average: { lte: 10 } } } } }) { content } } @@ -174,7 +178,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -195,7 +200,7 @@ describe("Cypher Aggregations where node with Int", () => { test("SUM_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_SUM_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { sum: { eq: 10 } } } } }) { content } } @@ -204,7 +209,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -228,7 +234,7 @@ describe("Cypher Aggregations where node with Int", () => { test("SUM_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_SUM_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { sum: { gt: 10 } } } } }) { content } } @@ -237,7 +243,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -261,7 +268,7 @@ describe("Cypher Aggregations where node with Int", () => { test("SUM_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_SUM_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { sum: { gte: 10 } } } } }) { content } } @@ -270,7 +277,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -294,7 +302,7 @@ describe("Cypher Aggregations where node with Int", () => { test("SUM_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_SUM_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { sum: { lt: 10 } } } } }) { content } } @@ -303,7 +311,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -327,7 +336,7 @@ describe("Cypher Aggregations where node with Int", () => { test("SUM_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_SUM_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { sum: { lte: 10 } } } } }) { content } } @@ -336,7 +345,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -360,7 +370,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MIN_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { min: { eq: 10 } } } } }) { content } } @@ -369,7 +379,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -393,7 +404,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MIN_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { min: { gt: 10 } } } } }) { content } } @@ -402,7 +413,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -426,7 +438,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MIN_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { min: { gte: 10 } } } } }) { content } } @@ -435,7 +447,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -459,7 +472,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MIN_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { min: { lt: 10 } } } } }) { content } } @@ -468,7 +481,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -492,7 +506,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MIN_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { min: { lte: 10 } } } } }) { content } } @@ -501,7 +515,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -525,7 +540,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MAX_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { max: { eq: 10 } } } } }) { content } } @@ -534,7 +549,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -558,7 +574,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MAX_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { max: { gt: 10 } } } } }) { content } } @@ -567,7 +583,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -591,7 +608,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MAX_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { max: { gte: 10 } } } } }) { content } } @@ -600,7 +617,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -624,7 +642,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MAX_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { max: { lt: 10 } } } } }) { content } } @@ -633,7 +651,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -657,7 +676,7 @@ describe("Cypher Aggregations where node with Int", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someInt_MAX_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { someInt: { max: { lte: 10 } } } } }) { content } } @@ -666,7 +685,8 @@ describe("Cypher Aggregations where node with Int", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/localdatetime.test.ts b/packages/graphql/tests/tck/aggregations/where/node/localdatetime.test.ts index 8d026a5b9d..af98b44bc6 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/localdatetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/localdatetime.test.ts @@ -45,7 +45,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MIN_EQUAL: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { min: { eq: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -54,7 +56,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -83,7 +86,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MIN_GT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { min: { gt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -92,7 +97,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -121,7 +127,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MIN_GTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { min: { gte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -130,7 +138,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -159,7 +168,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MIN_LT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { min: { lt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -168,7 +179,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -197,7 +209,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MIN_LTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { min: { lte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -206,7 +220,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -235,7 +250,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MAX_EQUAL: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { max: { eq: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -244,7 +261,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -273,7 +291,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MAX_GT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { max: { gt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -282,7 +302,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -311,7 +332,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MAX_GTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { max: { gte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -320,7 +343,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -349,7 +373,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MAX_LT: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { max: { lt: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -358,7 +384,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -387,7 +414,9 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalDateTime_MAX_LTE: "2003-09-14T12:00:00" } } }) { + posts( + where: { likesAggregate: { node: { someLocalDateTime: { max: { lte: "2003-09-14T12:00:00" } } } } } + ) { content } } @@ -396,7 +425,8 @@ describe("Cypher Aggregations where node with LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/localtime.test.ts b/packages/graphql/tests/tck/aggregations/where/node/localtime.test.ts index 37ddd744d5..f8f023ab12 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/localtime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/localtime.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MIN_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { min: { eq: "12:00:00" } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -80,7 +81,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MIN_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { min: { gt: "12:00:00" } } } } }) { content } } @@ -89,7 +90,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -115,7 +117,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MIN_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { min: { gte: "12:00:00" } } } } }) { content } } @@ -124,7 +126,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -150,7 +153,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MIN_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { min: { lt: "12:00:00" } } } } }) { content } } @@ -159,7 +162,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -185,7 +189,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MIN_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { min: { lte: "12:00:00" } } } } }) { content } } @@ -194,7 +198,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -220,7 +225,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MAX_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { max: { eq: "12:00:00" } } } } }) { content } } @@ -229,7 +234,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -255,7 +261,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MAX_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { max: { gt: "12:00:00" } } } } }) { content } } @@ -264,7 +270,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -290,7 +297,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MAX_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { max: { gte: "12:00:00" } } } } }) { content } } @@ -299,7 +306,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -325,7 +333,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MAX_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { max: { lt: "12:00:00" } } } } }) { content } } @@ -334,7 +342,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -360,7 +369,7 @@ describe("Cypher Aggregations where node with LocalTime", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someLocalTime_MAX_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someLocalTime: { max: { lte: "12:00:00" } } } } }) { content } } @@ -369,7 +378,8 @@ describe("Cypher Aggregations where node with LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) diff --git a/packages/graphql/tests/tck/aggregations/where/node/string.test.ts b/packages/graphql/tests/tck/aggregations/where/node/string.test.ts index 62fa509f73..a4cef0ccea 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/string.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/string.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with String", () => { test("SHORTEST_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { eq: 10 } } } } }) { content } } @@ -54,7 +54,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -78,7 +79,7 @@ describe("Cypher Aggregations where node with String", () => { test("SHORTEST_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { gt: 10 } } } } }) { content } } @@ -87,7 +88,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -111,7 +113,7 @@ describe("Cypher Aggregations where node with String", () => { test("SHORTEST_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { gte: 10 } } } } }) { content } } @@ -120,7 +122,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -144,7 +147,7 @@ describe("Cypher Aggregations where node with String", () => { test("SHORTEST_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { lt: 10 } } } } }) { content } } @@ -153,7 +156,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -177,7 +181,7 @@ describe("Cypher Aggregations where node with String", () => { test("SHORTEST_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { lte: 10 } } } } }) { content } } @@ -186,7 +190,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -210,7 +215,7 @@ describe("Cypher Aggregations where node with String", () => { test("LONGEST_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { eq: 10 } } } } }) { content } } @@ -219,7 +224,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -243,7 +249,7 @@ describe("Cypher Aggregations where node with String", () => { test("LONGEST_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { gt: 10 } } } } }) { content } } @@ -252,7 +258,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -276,7 +283,7 @@ describe("Cypher Aggregations where node with String", () => { test("LONGEST_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { gte: 10 } } } } }) { content } } @@ -285,7 +292,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -309,7 +317,7 @@ describe("Cypher Aggregations where node with String", () => { test("LONGEST_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { lt: 10 } } } } }) { content } } @@ -318,7 +326,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -342,7 +351,7 @@ describe("Cypher Aggregations where node with String", () => { test("LONGEST_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { lte: 10 } } } } }) { content } } @@ -351,7 +360,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -375,7 +385,7 @@ describe("Cypher Aggregations where node with String", () => { test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { eq: 10 } } } } }) { content } } @@ -384,7 +394,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -405,7 +416,7 @@ describe("Cypher Aggregations where node with String", () => { test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { gt: 10 } } } } }) { content } } @@ -414,7 +425,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -435,7 +447,7 @@ describe("Cypher Aggregations where node with String", () => { test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { gte: 10 } } } } }) { content } } @@ -444,7 +456,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -465,7 +478,7 @@ describe("Cypher Aggregations where node with String", () => { test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { lt: 10 } } } } }) { content } } @@ -474,7 +487,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -495,7 +509,7 @@ describe("Cypher Aggregations where node with String", () => { test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { lte: 10 } } } } }) { content } } @@ -504,7 +518,8 @@ describe("Cypher Aggregations where node with String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -558,7 +573,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("SHORTEST_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { eq: 10 } } } } }) { content } } @@ -567,7 +582,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -592,7 +608,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("SHORTEST_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { gt: 10 } } } } }) { content } } @@ -601,7 +617,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -626,7 +643,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("SHORTEST_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { gte: 10 } } } } }) { content } } @@ -635,7 +652,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -660,7 +678,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("SHORTEST_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { lt: 10 } } } } }) { content } } @@ -669,7 +687,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -694,7 +713,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("SHORTEST_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_SHORTEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { shortestLength: { lte: 10 } } } } }) { content } } @@ -703,7 +722,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -728,7 +748,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("LONGEST_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { eq: 10 } } } } }) { content } } @@ -737,7 +757,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -762,7 +783,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("LONGEST_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { gt: 10 } } } } }) { content } } @@ -771,7 +792,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -796,7 +818,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("LONGEST_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { gte: 10 } } } } }) { content } } @@ -805,7 +827,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -830,7 +853,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("LONGEST_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { lt: 10 } } } } }) { content } } @@ -839,7 +862,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -864,7 +888,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("LONGEST_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_LONGEST_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { longestLength: { lte: 10 } } } } }) { content } } @@ -873,7 +897,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -898,7 +923,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("AVERAGE_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_EQUAL: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { eq: 10 } } } } }) { content } } @@ -907,7 +932,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -929,7 +955,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("AVERAGE_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_GT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { gt: 10 } } } } }) { content } } @@ -938,7 +964,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -960,7 +987,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("AVERAGE_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_GTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { gte: 10 } } } } }) { content } } @@ -969,7 +996,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -991,7 +1019,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("AVERAGE_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_LT: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { lt: 10 } } } } }) { content } } @@ -1000,7 +1028,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) @@ -1022,7 +1051,7 @@ describe("Cypher Aggregations where node with String interface relationships of test("AVERAGE_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { name_AVERAGE_LENGTH_LTE: 10 } } }) { + posts(where: { likesAggregate: { node: { name: { averageLength: { lte: 10 } } } } }) { content } } @@ -1031,7 +1060,8 @@ describe("Cypher Aggregations where node with String interface relationships of const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1) diff --git a/packages/graphql/tests/tck/aggregations/where/node/time.test.ts b/packages/graphql/tests/tck/aggregations/where/node/time.test.ts index 8108c6d8da..04364d1bc8 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/time.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/time.test.ts @@ -45,7 +45,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MIN_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { min: { eq: "12:00:00" } } } } }) { content } } @@ -54,11 +54,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) = $param0 AS var2 + RETURN min(this1.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -67,13 +68,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -81,7 +76,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MIN_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MIN_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { min: { gt: "12:00:00" } } } } }) { content } } @@ -90,11 +85,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) > $param0 AS var2 + RETURN min(this1.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -103,13 +99,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -117,7 +107,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MIN_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MIN_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { min: { gte: "12:00:00" } } } } }) { content } } @@ -126,11 +116,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) >= $param0 AS var2 + RETURN min(this1.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -139,13 +130,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -153,7 +138,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MIN_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MIN_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { min: { lt: "12:00:00" } } } } }) { content } } @@ -162,11 +147,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) < $param0 AS var2 + RETURN min(this1.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -175,13 +161,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -189,7 +169,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MIN_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MIN_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { min: { lte: "12:00:00" } } } } }) { content } } @@ -198,11 +178,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) <= $param0 AS var2 + RETURN min(this1.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -211,13 +192,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -225,7 +200,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MAX_EQUAL", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MAX_EQUAL: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { max: { eq: "12:00:00" } } } } }) { content } } @@ -234,11 +209,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) = $param0 AS var2 + RETURN max(this1.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -247,13 +223,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -261,7 +231,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MAX_GT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MAX_GT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { max: { gt: "12:00:00" } } } } }) { content } } @@ -270,11 +240,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) > $param0 AS var2 + RETURN max(this1.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -283,13 +254,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -297,7 +262,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MAX_GTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MAX_GTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { max: { gte: "12:00:00" } } } } }) { content } } @@ -306,11 +271,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) >= $param0 AS var2 + RETURN max(this1.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -319,13 +285,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -333,7 +293,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MAX_LT", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MAX_LT: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { max: { lt: "12:00:00" } } } } }) { content } } @@ -342,11 +302,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) < $param0 AS var2 + RETURN max(this1.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -355,13 +316,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -369,7 +324,7 @@ describe("Cypher Aggregations where node with Time", () => { test("MAX_LTE", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { node: { someTime_MAX_LTE: "12:00:00" } } }) { + posts(where: { likesAggregate: { node: { someTime: { max: { lte: "12:00:00" } } } } }) { content } } @@ -378,11 +333,12 @@ describe("Cypher Aggregations where node with Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) <= $param0 AS var2 + RETURN max(this1.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -391,13 +347,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); diff --git a/packages/graphql/tests/tck/alias.test.ts b/packages/graphql/tests/tck/alias.test.ts index f7369db85b..a425c7e015 100644 --- a/packages/graphql/tests/tck/alias.test.ts +++ b/packages/graphql/tests/tck/alias.test.ts @@ -70,7 +70,8 @@ describe("Cypher Alias", () => { // NOTE: Order of these subqueries have been reversed after refactor expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -86,6 +87,7 @@ describe("Cypher Alias", () => { CALL { WITH this MATCH (this)<-[this2:ACTED_IN]-(this3:Actor) + WITH DISTINCT this3 WITH this3 { aliasActorsName: this3.name } AS this3 RETURN collect(this3) AS var4 } diff --git a/packages/graphql/tests/tck/array-methods.test.ts b/packages/graphql/tests/tck/array-methods.test.ts index 36b7dc4e28..01848b623e 100644 --- a/packages/graphql/tests/tck/array-methods.test.ts +++ b/packages/graphql/tests/tck/array-methods.test.ts @@ -48,7 +48,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.ratings IS NULL, \\"Property %s cannot be NULL\\", ['ratings']) SET this.ratings = this.ratings + $this_update_ratings_PUSH @@ -93,7 +94,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.ratings IS NULL OR this.scores IS NULL, \\"Properties %s, %s cannot be NULL\\", ['ratings', 'scores']) SET this.ratings = this.ratings + $this_update_ratings_PUSH @@ -152,7 +154,8 @@ describe("Arrays Methods", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.filmingLocations IS NULL, \\"Property %s cannot be NULL\\", ['filmingLocations']) SET this.filmingLocations = this.filmingLocations + [p in $this_update_filmingLocations_PUSH | point(p)] @@ -182,7 +185,9 @@ describe("Arrays Methods", () => { type Movie @node { title: String! ratings: [Float!]! - @authorization(validate: [{ operations: [UPDATE], where: { jwt: { roles_INCLUDES: "update" } } }]) + @authorization( + validate: [{ operations: [UPDATE], where: { jwt: { roles: { includes: "update" } } } }] + ) } `; @@ -207,7 +212,8 @@ describe("Arrays Methods", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(this.ratings IS NULL, \\"Property %s cannot be NULL\\", ['ratings']) SET this.ratings = this.ratings + $this_update_ratings_PUSH @@ -258,7 +264,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.ratings IS NULL, \\"Property %s cannot be NULL\\", ['ratings']) SET this.ratings = this.ratings[0..-$this_update_ratings_POP] @@ -304,7 +311,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.ratings IS NULL OR this.scores IS NULL, \\"Properties %s, %s cannot be NULL\\", ['ratings', 'scores']) SET this.ratings = this.ratings[0..-$this_update_ratings_POP] @@ -336,7 +344,9 @@ describe("Arrays Methods", () => { type Movie @node { title: String! ratings: [Float!]! - @authorization(validate: [{ operations: [UPDATE], where: { jwt: { roles_INCLUDES: "update" } } }]) + @authorization( + validate: [{ operations: [UPDATE], where: { jwt: { roles: { includes: "update" } } } }] + ) } `; @@ -361,7 +371,8 @@ describe("Arrays Methods", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(this.ratings IS NULL, \\"Property %s cannot be NULL\\", ['ratings']) SET this.ratings = this.ratings[0..-$this_update_ratings_POP] @@ -415,7 +426,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this WHERE apoc.util.validatePredicate(this.ratings IS NULL OR this.scores IS NULL, \\"Properties %s, %s cannot be NULL\\", ['ratings', 'scores']) SET this.ratings = this.ratings + $this_update_ratings_PUSH @@ -451,7 +463,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -461,7 +473,7 @@ describe("Arrays Methods", () => { const query = /* GraphQL */ ` mutation { - updateActors(where: { id_EQ: 1 }, update: { actedIn: [{ update: { edge: { pay_PUSH: 10 } } }] }) { + updateActors(where: { id: { eq: 1 } }, update: { actedIn: [{ update: { edge: { pay_PUSH: 10 } } }] }) { actors { name actedIn { @@ -482,7 +494,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.id = $param0 WITH this CALL { @@ -495,6 +508,7 @@ describe("Arrays Methods", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -553,7 +567,7 @@ describe("Arrays Methods", () => { } type ActedIn @relationshipProperties { - pay: [Float] + pay: [Float!] } `; @@ -563,7 +577,7 @@ describe("Arrays Methods", () => { const query = /* GraphQL */ ` mutation { - updateActors(where: { id_EQ: 1 }, update: { actedIn: [{ update: { edge: { pay_POP: 1 } } }] }) { + updateActors(where: { id: { eq: 1 } }, update: { actedIn: [{ update: { edge: { pay_POP: 1 } } }] }) { actors { name actedIn { @@ -584,7 +598,8 @@ describe("Arrays Methods", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.id = $param0 WITH this CALL { @@ -597,6 +612,7 @@ describe("Arrays Methods", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } diff --git a/packages/graphql/tests/tck/arrays.test.ts b/packages/graphql/tests/tck/arrays.test.ts index ff6c94504e..6c156da881 100644 --- a/packages/graphql/tests/tck/arrays.test.ts +++ b/packages/graphql/tests/tck/arrays.test.ts @@ -40,7 +40,7 @@ describe("Cypher Arrays", () => { test("WHERE INCLUDES", async () => { const query = /* GraphQL */ ` { - movies(where: { ratings_INCLUDES: 4.0 }) { + movies(where: { ratings: { includes: 4.0 } }) { title ratings } @@ -50,7 +50,8 @@ describe("Cypher Arrays", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE $param0 IN this.ratings RETURN this { .title, .ratings } AS this" `); @@ -61,6 +62,4 @@ describe("Cypher Arrays", () => { }" `); }); - - }); diff --git a/packages/graphql/tests/tck/connections/alias.test.ts b/packages/graphql/tests/tck/connections/alias.test.ts index f55a794564..fc631596ab 100644 --- a/packages/graphql/tests/tck/connections/alias.test.ts +++ b/packages/graphql/tests/tck/connections/alias.test.ts @@ -60,7 +60,8 @@ describe("Connections Alias", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -83,9 +84,9 @@ describe("Connections Alias", () => { test("Alias Top Level Connection Field Multiple Times", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title - hanks: actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + hanks: actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime @@ -95,7 +96,7 @@ describe("Connections Alias", () => { } } } - jenny: actorsConnection(where: { node: { name_EQ: "Robin Wright" } }) { + jenny: actorsConnection(where: { node: { name: { eq: "Robin Wright" } } }) { edges { properties { screenTime @@ -112,7 +113,8 @@ describe("Connections Alias", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts deleted file mode 100644 index 0e01296f71..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-alias.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Connect or create with @alias", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type BibliographicReference @node(labels: ["BibliographicReference", "Resource"]) { - iri: ID! @unique @alias(property: "_uri") - prefLabel: [String] - isInPublication: [Concept!]! @relationship(type: "isInPublication", direction: OUT) - } - - type Concept @node(labels: ["Concept", "Resource"]) { - iri: ID! @unique @alias(property: "$_uri") - prefLabel: [String]! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("where with multiple filters and params", async () => { - const query = /* GraphQL */ ` - mutation { - updateBibliographicReferences( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel_SET: "Updated Label:My BRS with Resource" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - bibliographicReferences { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:BibliographicReference:Resource) - WHERE this._uri = $param0 - SET this.prefLabel = $this_update_prefLabel_SET - WITH this - CALL { - WITH this - MERGE (this_isInPublication0_connectOrCreate0:Concept:Resource { \`$_uri\`: $this_isInPublication0_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication0_connectOrCreate0.\`$_uri\` = $this_isInPublication0_connectOrCreate_param1, - this_isInPublication0_connectOrCreate0.prefLabel = $this_isInPublication0_connectOrCreate_param2 - MERGE (this)-[this_isInPublication0_connectOrCreate_this0:isInPublication]->(this_isInPublication0_connectOrCreate0) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_isInPublication1_connectOrCreate0:Concept:Resource { \`$_uri\`: $this_isInPublication1_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication1_connectOrCreate0.\`$_uri\` = $this_isInPublication1_connectOrCreate_param1, - this_isInPublication1_connectOrCreate0.prefLabel = $this_isInPublication1_connectOrCreate_param2 - MERGE (this)-[this_isInPublication1_connectOrCreate_this0:isInPublication]->(this_isInPublication1_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:isInPublication]->(update_this1:Concept:Resource) - WHERE update_this1.\`$_uri\` IN $update_param0 - WITH update_this1 { .prefLabel, iri: update_this1.\`$_uri\` } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .prefLabel, iri: this._uri, isInPublication: update_var2 }) AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"update_param0\\": [ - \\"new-f\\", - \\"new-e\\" - ], - \\"param0\\": \\"urn:myiri2\\", - \\"this_update_prefLabel_SET\\": [ - \\"Updated Label:My BRS with Resource\\" - ], - \\"this_isInPublication0_connectOrCreate_param0\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param1\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"this_isInPublication1_connectOrCreate_param0\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param1\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts deleted file mode 100644 index 5e6b7745a5..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-auth.test.ts +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../../src"; -import { createBearerToken } from "../../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("connectOrCreate", () => { - const secret = "secret"; - let neoSchema: Neo4jGraphQL; - - function createTypedef(operations: string): string { - return /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type Movie @node { - title: String - genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type Genre @authorization(validate: [{ operations: ${operations}, where: { jwt: { roles_INCLUDES: "admin" } } }]) @node { - name: String @unique - } - `; - } - - describe("Create -> nested connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { - title: "Cool Movie" - genres: { - connectOrCreate: [ - { where: { node: { name_EQ: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ] - ) { - movies { - title - } - } - } - `; - - test("Create with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this0_genres_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this0_genres_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_genres_connectOrCreate0:Genre { name: $this0_genres_connectOrCreate_param0 }) - ON CREATE SET - this0_genres_connectOrCreate0.name = $this0_genres_connectOrCreate_param1 - MERGE (this0)-[this0_genres_connectOrCreate_this0:IN_GENRE]->(this0_genres_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Cool Movie\\", - \\"this0_genres_connectOrCreate_param0\\": \\"Horror\\", - \\"this0_genres_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Update -> nested connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - update: { - title_SET: "Cool Movie" - genres: { - connectOrCreate: [ - { where: { node: { name: "Horror" } }, onCreate: { node: { name: "Horror" } } } - ] - } - } - ) { - movies { - title - } - } - } - `; - - test("Update with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Update -> connectOrCreate", () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - update: { - title_SET: "Cool Movie" - genres: { - connectOrCreate: { - where: { node: { name: "Horror" } } - onCreate: { node: { name: "Horror" } } - } - } - } - ) { - movies { - title - } - } - } - `; - - test("Update with createOrConnect and CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect and CREATE, CONNECT operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[CREATE, CREATE_RELATIONSHIP]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_genres0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_genres0_connectOrCreate_param4\\": \\"admin\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and DELETE operation rule", async () => { - neoSchema = new Neo4jGraphQL({ - typeDefs: createTypedef("[DELETE]"), - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", {}); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect and allow in auth", async () => { - const typeDefs = /* GraphQL */ ` - type Movie @node { - title: String - genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) - } - - type Genre @node { - name: String @unique - } - - extend type Genre - @authorization( - validate: [{ when: [BEFORE], operations: [CREATE], where: { node: { name: "$jwt.sub" } } }] - ) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const token = createBearerToken("secret", { - sub: "test", - }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.title = $this_update_title_SET - WITH this - CALL { - WITH this - MERGE (this_genres0_connectOrCreate0:Genre { name: $this_genres0_connectOrCreate_param0 }) - ON CREATE SET - this_genres0_connectOrCreate0.name = $this_genres0_connectOrCreate_param1 - MERGE (this)-[this_genres0_connectOrCreate_this0:IN_GENRE]->(this_genres0_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .title }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_update_title_SET\\": \\"Cool Movie\\", - \\"this_genres0_connectOrCreate_param0\\": \\"Horror\\", - \\"this_genres0_connectOrCreate_param1\\": \\"Horror\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts deleted file mode 100644 index ad48698edf..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create-unions.test.ts +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Create or connect with unions", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! - isan: String! @unique - } - - type Series @node { - title: String! - isan: String! @unique - } - - union Production = Movie | Series - - type ActedIn @relationshipProperties { - screentime: Int! - } - - type Actor @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - actedIn: { - Movie: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0000-03B6-0000-O-0000-0006-P" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "0000-0000-03B6-0000-O-0000-0006-P" } - } - } - } - Series: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0001-ECC5-0000-8-0000-0001-B" } } - onCreate: { - edge: { screentime: 126 } - node: { - title: "Band of Brothers" - isan: "0000-0001-ECC5-0000-8-0000-0001-B" - } - } - } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_actedIn_Movie_connectOrCreate0:Movie { isan: $this0_actedIn_Movie_connectOrCreate_param0 }) - ON CREATE SET - this0_actedIn_Movie_connectOrCreate0.title = $this0_actedIn_Movie_connectOrCreate_param1, - this0_actedIn_Movie_connectOrCreate0.isan = $this0_actedIn_Movie_connectOrCreate_param2 - MERGE (this0)-[this0_actedIn_Movie_connectOrCreate_this0:ACTED_IN]->(this0_actedIn_Movie_connectOrCreate0) - ON CREATE SET - this0_actedIn_Movie_connectOrCreate_this0.screentime = $this0_actedIn_Movie_connectOrCreate_param3 - RETURN count(*) AS _ - } - WITH this0 - CALL { - WITH this0 - MERGE (this0_actedIn_Series_connectOrCreate0:Series { isan: $this0_actedIn_Series_connectOrCreate_param0 }) - ON CREATE SET - this0_actedIn_Series_connectOrCreate0.title = $this0_actedIn_Series_connectOrCreate_param1, - this0_actedIn_Series_connectOrCreate0.isan = $this0_actedIn_Series_connectOrCreate_param2 - MERGE (this0)-[this0_actedIn_Series_connectOrCreate_this0:ACTED_IN]->(this0_actedIn_Series_connectOrCreate0) - ON CREATE SET - this0_actedIn_Series_connectOrCreate_this0.screentime = $this0_actedIn_Series_connectOrCreate_param3 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_actedIn_Movie_connectOrCreate_param0\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this0_actedIn_Movie_connectOrCreate_param1\\": \\"Forrest Gump\\", - \\"this0_actedIn_Movie_connectOrCreate_param2\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this0_actedIn_Movie_connectOrCreate_param3\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"this0_actedIn_Series_connectOrCreate_param0\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this0_actedIn_Series_connectOrCreate_param1\\": \\"Band of Brothers\\", - \\"this0_actedIn_Series_connectOrCreate_param2\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this0_actedIn_Series_connectOrCreate_param3\\": { - \\"low\\": 126, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name_SET: "Tom Hanks" - actedIn: { - Movie: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0000-03B6-0000-O-0000-0006-P" } } - onCreate: { - edge: { screentime: 105 } - node: { title: "Forrest Gump", isan: "0000-0000-03B6-0000-O-0000-0006-P" } - } - } - } - Series: { - connectOrCreate: { - where: { node: { isan_EQ: "0000-0001-ECC5-0000-8-0000-0001-B" } } - onCreate: { - edge: { screentime: 126 } - node: { title: "Band of Brothers", isan: "0000-0001-ECC5-0000-8-0000-0001-B" } - } - } - } - } - } - where: { name_EQ: "Tom Hanks evil twin" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH this - CALL { - WITH this - MERGE (this_actedIn_Movie0_connectOrCreate0:Movie { isan: $this_actedIn_Movie0_connectOrCreate_param0 }) - ON CREATE SET - this_actedIn_Movie0_connectOrCreate0.title = $this_actedIn_Movie0_connectOrCreate_param1, - this_actedIn_Movie0_connectOrCreate0.isan = $this_actedIn_Movie0_connectOrCreate_param2 - MERGE (this)-[this_actedIn_Movie0_connectOrCreate_this0:ACTED_IN]->(this_actedIn_Movie0_connectOrCreate0) - ON CREATE SET - this_actedIn_Movie0_connectOrCreate_this0.screentime = $this_actedIn_Movie0_connectOrCreate_param3 - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_actedIn_Series0_connectOrCreate0:Series { isan: $this_actedIn_Series0_connectOrCreate_param0 }) - ON CREATE SET - this_actedIn_Series0_connectOrCreate0.title = $this_actedIn_Series0_connectOrCreate_param1, - this_actedIn_Series0_connectOrCreate0.isan = $this_actedIn_Series0_connectOrCreate_param2 - MERGE (this)-[this_actedIn_Series0_connectOrCreate_this0:ACTED_IN]->(this_actedIn_Series0_connectOrCreate0) - ON CREATE SET - this_actedIn_Series0_connectOrCreate_this0.screentime = $this_actedIn_Series0_connectOrCreate_param3 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks evil twin\\", - \\"this_update_name_SET\\": \\"Tom Hanks\\", - \\"this_actedIn_Movie0_connectOrCreate_param0\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this_actedIn_Movie0_connectOrCreate_param1\\": \\"Forrest Gump\\", - \\"this_actedIn_Movie0_connectOrCreate_param2\\": \\"0000-0000-03B6-0000-O-0000-0006-P\\", - \\"this_actedIn_Movie0_connectOrCreate_param3\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"this_actedIn_Series0_connectOrCreate_param0\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this_actedIn_Series0_connectOrCreate_param1\\": \\"Band of Brothers\\", - \\"this_actedIn_Series0_connectOrCreate_param2\\": \\"0000-0001-ECC5-0000-8-0000-0001-B\\", - \\"this_actedIn_Series0_connectOrCreate_param3\\": { - \\"low\\": 126, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts b/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts deleted file mode 100644 index 93d64bcab2..0000000000 --- a/packages/graphql/tests/tck/connections/connect-or-create/connect-or-create.test.ts +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../../src"; -import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; - -describe("Create or Connect", () => { - describe("Simple", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name_SET: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name_SET\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Autogenerated field on created node", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.createdAt = datetime(), - this0_movies_connectOrCreate0.id = randomUUID(), - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Create with createOrConnect operation - with @id in where", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "movieId" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { id: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.createdAt = datetime(), - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"movieId\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name_SET: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.createdAt = datetime(), - this_movies0_connectOrCreate0.id = randomUUID(), - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name_SET\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation - with @id in where", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name_SET: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { id_EQ: "movieId" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { id: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.createdAt = datetime(), - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name_SET\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"movieId\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("Autogenerated field on created relationship", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - title: String! @unique - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type Actor @node { - name: String! - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type ActedIn @relationshipProperties { - id: ID! @id - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime @timestamp(operations: [UPDATE]) - screentime: Int! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Create with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - createActors( - input: [ - { - name: "Tom Hanks" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - ] - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Actor) - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_movies_connectOrCreate0:Movie { title: $this0_movies_connectOrCreate_param0 }) - ON CREATE SET - this0_movies_connectOrCreate0.title = $this0_movies_connectOrCreate_param1 - MERGE (this0)-[this0_movies_connectOrCreate_this0:ACTED_IN]->(this0_movies_connectOrCreate0) - ON CREATE SET - this0_movies_connectOrCreate_this0.createdAt = datetime(), - this0_movies_connectOrCreate_this0.id = randomUUID(), - this0_movies_connectOrCreate_this0.screentime = $this0_movies_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Tom Hanks\\", - \\"this0_movies_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this0_movies_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update with createOrConnect operation", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - update: { - name_SET: "Tom Hanks 2" - movies: { - connectOrCreate: { - where: { node: { title_EQ: "The Terminal" } } - onCreate: { edge: { screentime: 105 }, node: { title: "The Terminal" } } - } - } - } - where: { name_EQ: "Tom Hanks" } - ) { - actors { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH this - CALL { - WITH this - MERGE (this_movies0_connectOrCreate0:Movie { title: $this_movies0_connectOrCreate_param0 }) - ON CREATE SET - this_movies0_connectOrCreate0.title = $this_movies0_connectOrCreate_param1 - MERGE (this)-[this_movies0_connectOrCreate_this0:ACTED_IN]->(this_movies0_connectOrCreate0) - ON CREATE SET - this_movies0_connectOrCreate_this0.createdAt = datetime(), - this_movies0_connectOrCreate_this0.id = randomUUID(), - this_movies0_connectOrCreate_this0.screentime = $this_movies0_connectOrCreate_param2 - RETURN count(*) AS _ - } - RETURN collect(DISTINCT this { .name }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Tom Hanks\\", - \\"this_update_name_SET\\": \\"Tom Hanks 2\\", - \\"this_movies0_connectOrCreate_param0\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param1\\": \\"The Terminal\\", - \\"this_movies0_connectOrCreate_param2\\": { - \\"low\\": 105, - \\"high\\": 0 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/connections/filtering/composite.test.ts b/packages/graphql/tests/tck/connections/filtering/composite.test.ts index ae64774029..7e0e2bc098 100644 --- a/packages/graphql/tests/tck/connections/filtering/composite.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/composite.test.ts @@ -50,12 +50,12 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { test("Composite", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection( where: { - node: { AND: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } - edge: { AND: [{ screenTime_GT: 30 }, { screenTime_LT: 90 }] } + node: { AND: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] } + edge: { AND: [{ screenTime: { gt: 30 } }, { screenTime: { lt: 90 } }] } } ) { edges { @@ -75,7 +75,8 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -114,12 +115,12 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { test("Composite NOT", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection( where: { - node: { NOT: { firstName_EQ: "Tom", lastName_EQ: "Hanks" } } - edge: { NOT: { screenTime_GT: 30, screenTime_LT: 90 } } + node: { NOT: { firstName: { eq: "Tom" }, lastName: { eq: "Hanks" } } } + edge: { NOT: { screenTime: { gt: 30, lt: 90 } } } } ) { edges { @@ -139,12 +140,13 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) - WHERE (NOT (this1.firstName = $param1 AND this1.lastName = $param2) AND NOT (this0.screenTime < $param3 AND this0.screenTime > $param4)) + WHERE (NOT (this1.firstName = $param1 AND this1.lastName = $param2) AND NOT (this0.screenTime > $param3 AND this0.screenTime < $param4)) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -164,11 +166,11 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { \\"param1\\": \\"Tom\\", \\"param2\\": \\"Hanks\\", \\"param3\\": { - \\"low\\": 90, + \\"low\\": 30, \\"high\\": 0 }, \\"param4\\": { - \\"low\\": 30, + \\"low\\": 90, \\"high\\": 0 } }" @@ -178,13 +180,13 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { test("Composite OR (edge and node)", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection( where: { OR: [ - { node: { AND: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } } - { edge: { AND: [{ screenTime_GT: 30 }, { screenTime_LT: 90 }] } } + { node: { AND: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] } } + { edge: { AND: [{ screenTime: { gt: 30 } }, { screenTime: { lt: 90 } }] } } ] } ) { @@ -205,7 +207,8 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -244,14 +247,14 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { test("Composite NOT with nested OR (edge and node)", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection( where: { NOT: { OR: [ - { node: { AND: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } } - { edge: { AND: [{ screenTime_GT: 30 }, { screenTime_LT: 90 }] } } + { node: { AND: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] } } + { edge: { AND: [{ screenTime: { gt: 30 } }, { screenTime: { lt: 90 } }] } } ] } } @@ -273,7 +276,8 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -312,7 +316,7 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { test("Composite NOT with complex nested filters", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection( where: { @@ -320,11 +324,15 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { AND: [ { OR: [ - { node: { AND: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } } - { edge: { AND: [{ screenTime_GT: 30 }, { screenTime_LT: 90 }] } } + { + node: { + AND: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] + } + } + { edge: { AND: [{ screenTime: { gt: 30 } }, { screenTime: { lt: 90 } }] } } ] } - { node: { AND: [{ firstName_EQ: "Tommy" }, { lastName_EQ: "Ford" }] } } + { node: { AND: [{ firstName: { eq: "Tommy" } }, { lastName: { eq: "Ford" } }] } } ] } } @@ -346,7 +354,8 @@ describe("Cypher -> Connections -> Filtering -> Composite", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/connections/filtering/interface-relationships.test.ts b/packages/graphql/tests/tck/connections/filtering/interface-relationships.test.ts index 872849d859..ec739bfda3 100644 --- a/packages/graphql/tests/tck/connections/filtering/interface-relationships.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/interface-relationships.test.ts @@ -46,14 +46,16 @@ describe("interface relationships with aliased fields", () => { type Actor @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) + currentlyActingIn: [Production!]! @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type ProtectedActor @node @authorization( - validate: [{ where: { node: { actedInConnection_SOME: { node: { title_EQ: "$jwt.title" } } } } }] + validate: [ + { where: { node: { actedInConnection: { some: { node: { title: { eq: "$jwt.title" } } } } } } } + ] ) { name: String! @alias(property: "dbName") actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") @@ -68,7 +70,7 @@ describe("interface relationships with aliased fields", () => { test("should read and return interface relationship fields with interface relationship filter SOME", async () => { const query = /* GraphQL */ ` query Actors($title: String) { - actors(where: { actedInConnection_SOME: { node: { title_EQ: $title } } }) { + actors(where: { actedInConnection: { some: { node: { title: { eq: $title } } } } }) { name actedIn { title @@ -88,7 +90,8 @@ describe("interface relationships with aliased fields", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE EXISTS { MATCH (this)-[this0:ACTED_IN]->(this1) WHERE (CASE @@ -126,7 +129,7 @@ describe("interface relationships with aliased fields", () => { test("delete", async () => { const query = /* GraphQL */ ` mutation deleteActors($title: String) { - deleteActors(where: { actedInConnection_SOME: { node: { title_EQ: $title } } }) { + deleteActors(where: { actedInConnection: { some: { node: { title: { eq: $title } } } } }) { nodesDeleted relationshipsDeleted } @@ -138,7 +141,8 @@ describe("interface relationships with aliased fields", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE EXISTS { MATCH (this)-[this0:ACTED_IN]->(this1) WHERE (CASE @@ -178,7 +182,8 @@ describe("interface relationships with aliased fields", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ProtectedActor) + "CYPHER 5 + MATCH (this:ProtectedActor) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)-[this1:ACTED_IN]->(this0) WHERE (($jwt.title IS NOT NULL AND CASE WHEN this0:Movie THEN this0.movieTitle diff --git a/packages/graphql/tests/tck/connections/filtering/node/and.test.ts b/packages/graphql/tests/tck/connections/filtering/node/and.test.ts index cb84dbcded..128cba28c7 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/and.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/and.test.ts @@ -52,7 +52,9 @@ describe("Cypher -> Connections -> Filtering -> Node -> AND", () => { query { movies { title - actorsConnection(where: { node: { AND: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } }) { + actorsConnection( + where: { node: { AND: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] } } + ) { edges { properties { screenTime @@ -70,7 +72,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> AND", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -101,7 +104,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> AND", () => { query { movies { title - actorsConnection(where: { node: { NOT: { firstName_EQ: "Tom" } } }) { + actorsConnection(where: { node: { NOT: { firstName: { eq: "Tom" } } } }) { edges { properties { screenTime @@ -119,7 +122,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> AND", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/arrays.test.ts b/packages/graphql/tests/tck/connections/filtering/node/arrays.test.ts index f5476967a9..4e31d6a561 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/arrays.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/arrays.test.ts @@ -52,7 +52,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Arrays", () => { query { movies { title - actorsConnection(where: { node: { name_IN: ["Tom Hanks", "Robin Wright"] } }) { + actorsConnection(where: { node: { name: { in: ["Tom Hanks", "Robin Wright"] } } }) { edges { properties { screenTime @@ -69,7 +69,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Arrays", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -102,7 +103,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Arrays", () => { query { movies { title - actorsConnection(where: { node: { favouriteColours_INCLUDES: "Blue" } }) { + actorsConnection(where: { node: { favouriteColours: { includes: "Blue" } } }) { edges { properties { screenTime @@ -120,7 +121,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Arrays", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/equality.test.ts b/packages/graphql/tests/tck/connections/filtering/node/equality.test.ts index d70e3797b0..0c02b5aae4 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/equality.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/equality.test.ts @@ -51,7 +51,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Equality", () => { query { movies { title - actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime @@ -68,7 +68,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Equality", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -98,7 +99,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Equality", () => { query { movies { title - actorsConnection(where: { node: { NOT: { name_EQ: "Tom Hanks" } } }) { + actorsConnection(where: { node: { NOT: { name: { eq: "Tom Hanks" } } } }) { edges { properties { screenTime @@ -115,7 +116,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Equality", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/numerical.test.ts b/packages/graphql/tests/tck/connections/filtering/node/numerical.test.ts index 5c72d242ab..2fe5beebb7 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/numerical.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/numerical.test.ts @@ -52,7 +52,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { query { movies { title - actorsConnection(where: { node: { age_LT: 60 } }) { + actorsConnection(where: { node: { age: { lt: 60 } } }) { edges { properties { screenTime @@ -70,7 +70,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -103,7 +104,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { query { movies { title - actorsConnection(where: { node: { age_LTE: 60 } }) { + actorsConnection(where: { node: { age: { lte: 60 } } }) { edges { properties { screenTime @@ -121,7 +122,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -154,7 +156,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { query { movies { title - actorsConnection(where: { node: { age_GT: 60 } }) { + actorsConnection(where: { node: { age: { gt: 60 } } }) { edges { properties { screenTime @@ -172,7 +174,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -205,7 +208,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { query { movies { title - actorsConnection(where: { node: { age_GTE: 60 } }) { + actorsConnection(where: { node: { age: { gte: 60 } } }) { edges { properties { screenTime @@ -223,7 +226,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Numerical", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/or.test.ts b/packages/graphql/tests/tck/connections/filtering/node/or.test.ts index 3da74289d7..ffdd522061 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/or.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/or.test.ts @@ -52,7 +52,9 @@ describe("Cypher -> Connections -> Filtering -> Node -> OR", () => { query { movies { title - actorsConnection(where: { node: { OR: [{ firstName_EQ: "Tom" }, { lastName_EQ: "Hanks" }] } }) { + actorsConnection( + where: { node: { OR: [{ firstName: { eq: "Tom" } }, { lastName: { eq: "Hanks" } }] } } + ) { edges { properties { screenTime @@ -70,7 +72,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/points.test.ts b/packages/graphql/tests/tck/connections/filtering/node/points.test.ts index 4b576192b1..c3b2a72d37 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/points.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/points.test.ts @@ -86,7 +86,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/relationship.test.ts b/packages/graphql/tests/tck/connections/filtering/node/relationship.test.ts index 7f69d45953..c9b226a23a 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/relationship.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/relationship.test.ts @@ -47,7 +47,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Relationship", () => { query { movies { title - actorsConnection(where: { node: { movies_SOME: { title_EQ: "Forrest Gump" } } }) { + actorsConnection(where: { node: { movies: { some: { title: { eq: "Forrest Gump" } } } } }) { edges { node { name @@ -61,7 +61,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/node/string.test.ts b/packages/graphql/tests/tck/connections/filtering/node/string.test.ts index f71fa6ed3c..a4ab27f77f 100644 --- a/packages/graphql/tests/tck/connections/filtering/node/string.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/node/string.test.ts @@ -75,7 +75,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -100,12 +101,12 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { `); }); - test("STARTS_WITH", async () => { + test("startsWith", async () => { const query = /* GraphQL */ ` query { movies { title - actorsConnection(where: { node: { name_STARTS_WITH: "Tom" } }) { + actorsConnection(where: { node: { name: { startsWith: "Tom" } } }) { edges { properties { screenTime @@ -122,7 +123,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -147,12 +149,12 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { `); }); - test("ENDS_WITH", async () => { + test("endsWith", async () => { const query = /* GraphQL */ ` query { movies { title - actorsConnection(where: { node: { name_ENDS_WITH: "Hanks" } }) { + actorsConnection(where: { node: { name: { endsWith: "Hanks" } } }) { edges { properties { screenTime @@ -169,7 +171,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -216,7 +219,8 @@ describe("Cypher -> Connections -> Filtering -> Node -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/and.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/and.test.ts index c435c1ac53..1e7369a0b3 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/and.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/and.test.ts @@ -52,7 +52,9 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> AND", () => { query { movies { title - actorsConnection(where: { edge: { AND: [{ role_ENDS_WITH: "Gump" }, { screenTime_LT: 60 }] } }) { + actorsConnection( + where: { edge: { AND: [{ role: { endsWith: "Gump" } }, { screenTime: { lt: 60 } }] } } + ) { edges { properties { role @@ -70,7 +72,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> AND", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -104,7 +107,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> AND", () => { query { movies { title - actorsConnection(where: { edge: { NOT: { role_ENDS_WITH: "Gump" } } }) { + actorsConnection(where: { edge: { NOT: { role: { endsWith: "Gump" } } } }) { edges { properties { role @@ -122,7 +125,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> AND", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/arrays.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/arrays.test.ts index 920a6fb52f..3836d7b1e3 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/arrays.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/arrays.test.ts @@ -52,7 +52,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Arrays", () => { query { movies { title - actorsConnection(where: { edge: { screenTime_IN: [60, 70] } }) { + actorsConnection(where: { edge: { screenTime: { in: [60, 70] } } }) { edges { properties { screenTime @@ -69,7 +69,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Arrays", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -108,7 +109,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Arrays", () => { query { movies { title - actorsConnection(where: { edge: { quotes_INCLUDES: "Life is like a box of chocolates" } }) { + actorsConnection(where: { edge: { quotes: { includes: "Life is like a box of chocolates" } } }) { edges { properties { screenTime @@ -125,7 +126,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Arrays", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/equality.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/equality.test.ts index 6c9fd63dde..498e205ee4 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/equality.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/equality.test.ts @@ -51,7 +51,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Equality", () => query { movies { title - actorsConnection(where: { edge: { screenTime_EQ: 60 } }) { + actorsConnection(where: { edge: { screenTime: { eq: 60 } } }) { edges { properties { screenTime @@ -68,7 +68,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Equality", () => const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -101,7 +102,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Equality", () => query { movies { title - actorsConnection(where: { edge: { NOT: { screenTime_EQ: 60 } } }) { + actorsConnection(where: { edge: { NOT: { screenTime: { eq: 60 } } } }) { edges { properties { screenTime @@ -118,7 +119,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Equality", () => const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/numerical.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/numerical.test.ts index 8763d6a39a..7bd8d0ac49 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/numerical.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/numerical.test.ts @@ -51,7 +51,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = query { movies { title - actorsConnection(where: { edge: { screenTime_LT: 60 } }) { + actorsConnection(where: { edge: { screenTime: { lt: 60 } } }) { edges { properties { screenTime @@ -68,7 +68,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -101,7 +102,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = query { movies { title - actorsConnection(where: { edge: { screenTime_LTE: 60 } }) { + actorsConnection(where: { edge: { screenTime: { lte: 60 } } }) { edges { properties { screenTime @@ -118,7 +119,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -151,7 +153,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = query { movies { title - actorsConnection(where: { edge: { screenTime_GT: 60 } }) { + actorsConnection(where: { edge: { screenTime: { gt: 60 } } }) { edges { properties { screenTime @@ -168,7 +170,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -201,7 +204,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = query { movies { title - actorsConnection(where: { edge: { screenTime_GTE: 60 } }) { + actorsConnection(where: { edge: { screenTime: { gte: 60 } } }) { edges { properties { screenTime @@ -218,7 +221,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Numerical", () = const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/or.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/or.test.ts index 965a7a40d6..847c897c68 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/or.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/or.test.ts @@ -52,7 +52,9 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> OR", () => { query { movies { title - actorsConnection(where: { edge: { OR: [{ role_ENDS_WITH: "Gump" }, { screenTime_LT: 60 }] } }) { + actorsConnection( + where: { edge: { OR: [{ role: { endsWith: "Gump" } }, { screenTime: { lt: 60 } }] } } + ) { edges { properties { role @@ -70,7 +72,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -104,7 +107,9 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> OR", () => { { movies( where: { - actorsConnection_SOME: { OR: [{ node: { name_EQ: "Harry" } }, { edge: { role_EQ: "Tom" } }] } + actorsConnection: { + some: { OR: [{ node: { name: { eq: "Harry" } } }, { edge: { role: { eq: "Tom" } } }] } + } } ) { title @@ -115,7 +120,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> OR", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE EXISTS { MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) WHERE (this1.name = $param0 OR this0.role = $param1) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/points.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/points.test.ts index 78deb4c154..211930e4e8 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/points.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/points.test.ts @@ -84,7 +84,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/string.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/string.test.ts index 78651562d8..95492e1721 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/string.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/string.test.ts @@ -76,7 +76,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -101,12 +102,12 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { `); }); - test("STARTS_WITH", async () => { + test("startsWith", async () => { const query = /* GraphQL */ ` query { movies { title - actorsConnection(where: { edge: { role_STARTS_WITH: "Forrest" } }) { + actorsConnection(where: { edge: { role: { startsWith: "Forrest" } } }) { edges { properties { role @@ -123,7 +124,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -148,14 +150,12 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { `); }); - - - test("ENDS_WITH", async () => { + test("endsWith", async () => { const query = /* GraphQL */ ` query { movies { title - actorsConnection(where: { edge: { role_ENDS_WITH: "Gump" } }) { + actorsConnection(where: { edge: { role: { endsWith: "Gump" } } }) { edges { properties { role @@ -172,7 +172,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -219,7 +220,8 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> String", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts index 4cb178d361..8a195874e2 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts @@ -53,7 +53,9 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Temporal", () => movies { title actorsConnection( - where: { edge: { startDate_GT: "2000-01-01", endDateTime_LT: "2010-01-01T00:00:00.000Z" } } + where: { + edge: { startDate: { gt: "2000-01-01" }, endDateTime: { lt: "2010-01-01T00:00:00.000Z" } } + } ) { edges { properties { @@ -72,11 +74,12 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Temporal", () => const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) - WHERE (this0.startDate > $param0 AND this0.endDateTime < $param1) + WHERE (this0.startDate > $param0 AND this0.endDateTime < datetime($param1)) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -97,16 +100,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Temporal", () => \\"month\\": 1, \\"day\\": 1 }, - \\"param1\\": { - \\"year\\": 2010, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param1\\": \\"2010-01-01T00:00:00.000Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/connections/interfaces.test.ts b/packages/graphql/tests/tck/connections/interfaces.test.ts index 3c01e103d9..077dbe8da9 100644 --- a/packages/graphql/tests/tck/connections/interfaces.test.ts +++ b/packages/graphql/tests/tck/connections/interfaces.test.ts @@ -83,7 +83,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -112,7 +113,7 @@ describe("Cypher -> Connections -> Interfaces", () => { query { actors { name - actedInConnection(where: { node: { title_STARTS_WITH: "The " } }) { + actedInConnection(where: { node: { title: { startsWith: "The " } } }) { edges { properties { screenTime @@ -135,7 +136,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -171,7 +173,7 @@ describe("Cypher -> Connections -> Interfaces", () => { query { actors { name - actedInConnection(where: { edge: { screenTime_GT: 60 } }) { + actedInConnection(where: { edge: { screenTime: { gt: 60 } } }) { edges { properties { screenTime @@ -194,7 +196,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -261,7 +264,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -320,7 +324,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -378,7 +383,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -436,7 +442,8 @@ describe("Cypher -> Connections -> Interfaces", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/connections/mixed-nesting.test.ts b/packages/graphql/tests/tck/connections/mixed-nesting.test.ts index 48934c98b2..ce6778c476 100644 --- a/packages/graphql/tests/tck/connections/mixed-nesting.test.ts +++ b/packages/graphql/tests/tck/connections/mixed-nesting.test.ts @@ -49,16 +49,16 @@ describe("Mixed nesting", () => { test("Connection -> Relationship", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title - actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime } node { name - movies(where: { NOT: { title_EQ: "Forrest Gump" } }) { + movies(where: { NOT: { title: { eq: "Forrest Gump" } } }) { title } } @@ -71,7 +71,8 @@ describe("Mixed nesting", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -87,6 +88,7 @@ describe("Mixed nesting", () => { WITH this1 MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) WHERE NOT (this3.title = $param2) + WITH DISTINCT this3 WITH this3 { .title } AS this3 RETURN collect(this3) AS var4 } @@ -109,20 +111,20 @@ describe("Mixed nesting", () => { test("Connection -> Connection -> Relationship", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title - actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime } node { name - moviesConnection(where: { node: { NOT: { title_EQ: "Forrest Gump" } } }) { + moviesConnection(where: { node: { NOT: { title: { eq: "Forrest Gump" } } } }) { edges { node { title - actors(where: { NOT: { name_EQ: "Tom Hanks" } }) { + actors(where: { NOT: { name: { eq: "Tom Hanks" } } }) { name } } @@ -138,7 +140,8 @@ describe("Mixed nesting", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -164,6 +167,7 @@ describe("Mixed nesting", () => { WITH this3 MATCH (this3)<-[this4:ACTED_IN]-(this5:Actor) WHERE NOT (this5.name = $param3) + WITH DISTINCT this5 WITH this5 { .name } AS this5 RETURN collect(this5) AS var6 } @@ -191,11 +195,11 @@ describe("Mixed nesting", () => { test("Relationship -> Connection", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title - actors(where: { name_EQ: "Tom Hanks" }) { + actors(where: { name: { eq: "Tom Hanks" } }) { name - moviesConnection(where: { node: { NOT: { title_EQ: "Forrest Gump" } } }) { + moviesConnection(where: { node: { NOT: { title: { eq: "Forrest Gump" } } } }) { edges { properties { screenTime @@ -213,12 +217,14 @@ describe("Mixed nesting", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) WHERE this1.name = $param1 + WITH DISTINCT this1 CALL { WITH this1 MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) diff --git a/packages/graphql/tests/tck/connections/projections/create.test.ts b/packages/graphql/tests/tck/connections/projections/create.test.ts index 2e36df8179..48964693a0 100644 --- a/packages/graphql/tests/tck/connections/projections/create.test.ts +++ b/packages/graphql/tests/tck/connections/projections/create.test.ts @@ -70,7 +70,8 @@ describe("Cypher -> Connections -> Projections -> Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -129,7 +130,8 @@ describe("Cypher -> Connections -> Projections -> Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -173,7 +175,7 @@ describe("Cypher -> Connections -> Projections -> Create", () => { createMovies(input: [{ title: "Forrest Gump" }, { title: "Toy Story" }]) { movies { title - actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime @@ -191,7 +193,8 @@ describe("Cypher -> Connections -> Projections -> Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) diff --git a/packages/graphql/tests/tck/connections/projections/projections.test.ts b/packages/graphql/tests/tck/connections/projections/projections.test.ts index 0fb7d99c75..f4c2b767f7 100644 --- a/packages/graphql/tests/tck/connections/projections/projections.test.ts +++ b/packages/graphql/tests/tck/connections/projections/projections.test.ts @@ -52,7 +52,7 @@ describe("Relay Cursor Connection projections", () => { test("edges not returned if not asked for", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { totalCount @@ -64,7 +64,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -92,7 +93,7 @@ describe("Relay Cursor Connection projections", () => { test("edges and totalCount returned if pageInfo asked for", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { pageInfo { @@ -109,7 +110,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -137,7 +139,7 @@ describe("Relay Cursor Connection projections", () => { test("Minimal edges returned if not asked for with pagination arguments", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection(first: 5) { totalCount @@ -149,7 +151,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -183,7 +186,7 @@ describe("Relay Cursor Connection projections", () => { test("edges not returned if not asked for on a union", async () => { const query = /* GraphQL */ ` query { - actors(where: { name_EQ: "Tom Hanks" }) { + actors(where: { name: { eq: "Tom Hanks" } }) { name productionsConnection { totalCount @@ -195,7 +198,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 CALL { WITH this @@ -227,7 +231,7 @@ describe("Relay Cursor Connection projections", () => { test("edges and totalCount returned if pageInfo asked for on a union", async () => { const query = /* GraphQL */ ` query { - actors(where: { name_EQ: "Tom Hanks" }) { + actors(where: { name: { eq: "Tom Hanks" } }) { name productionsConnection { pageInfo { @@ -244,7 +248,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 CALL { WITH this @@ -276,7 +281,7 @@ describe("Relay Cursor Connection projections", () => { test("totalCount is calculated and returned if asked for with edges", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { totalCount @@ -293,7 +298,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -321,7 +327,7 @@ describe("Relay Cursor Connection projections", () => { test("totalCount is calculated and returned if asked for with edges with pagination arguments", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection(first: 5) { totalCount @@ -338,7 +344,8 @@ describe("Relay Cursor Connection projections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/connections/projections/update.test.ts b/packages/graphql/tests/tck/connections/projections/update.test.ts index 3ec79c6dd9..1ddea1d4a1 100644 --- a/packages/graphql/tests/tck/connections/projections/update.test.ts +++ b/packages/graphql/tests/tck/connections/projections/update.test.ts @@ -49,7 +49,7 @@ describe("Cypher -> Connections -> Projections -> Update", () => { test("Connection can be selected following update Mutation", async () => { const query = /* GraphQL */ ` mutation { - updateMovies(where: { title_EQ: "Forrest Gump" }) { + updateMovies(where: { title: { eq: "Forrest Gump" } }) { movies { title actorsConnection { @@ -70,7 +70,8 @@ describe("Cypher -> Connections -> Projections -> Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/connections/relationship-properties.test.ts b/packages/graphql/tests/tck/connections/relationship-properties.test.ts index a807ffb9d4..b59444722b 100644 --- a/packages/graphql/tests/tck/connections/relationship-properties.test.ts +++ b/packages/graphql/tests/tck/connections/relationship-properties.test.ts @@ -50,7 +50,7 @@ describe("Relationship Properties Cypher", () => { test("Projecting node and relationship properties with no arguments", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { edges { @@ -69,7 +69,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -97,9 +98,9 @@ describe("Relationship Properties Cypher", () => { test("Projecting node and relationship properties with where argument", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title - actorsConnection(where: { node: { name_EQ: "Tom Hanks" } }) { + actorsConnection(where: { node: { name: { eq: "Tom Hanks" } } }) { edges { properties { screenTime @@ -116,7 +117,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -146,7 +148,7 @@ describe("Relationship Properties Cypher", () => { test("Projecting node and relationship properties with sort argument", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection(sort: { edge: { screenTime: DESC } }) { edges { @@ -165,7 +167,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -213,7 +216,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -255,7 +259,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -280,7 +285,7 @@ describe("Relationship Properties Cypher", () => { test("Projecting twice nested node and relationship properties with no arguments", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { edges { @@ -309,7 +314,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -350,7 +356,7 @@ describe("Relationship Properties Cypher", () => { test("Projecting thrice nested node and relationship properties with no arguments", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: "Forrest Gump" }) { + movies(where: { title: { eq: "Forrest Gump" } }) { title actorsConnection { edges { @@ -389,7 +395,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/connections/relationship_properties/connect.test.ts b/packages/graphql/tests/tck/connections/relationship_properties/connect.test.ts index 778aba2020..59edbcd45f 100644 --- a/packages/graphql/tests/tck/connections/relationship_properties/connect.test.ts +++ b/packages/graphql/tests/tck/connections/relationship_properties/connect.test.ts @@ -70,7 +70,8 @@ describe("Relationship Properties Connect Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -84,7 +85,7 @@ describe("Relationship Properties Connect Cypher", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actors_connect0_node - MERGE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) + CREATE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) SET this0_actors_connect0_relationship.screenTime = $this0_actors_connect0_relationship_screenTime } } @@ -133,7 +134,7 @@ describe("Relationship Properties Connect Cypher", () => { { title: "Forrest Gump" actors: { - connect: [{ where: { node: { name_EQ: "Tom Hanks" } }, edge: { screenTime: 60 } }] + connect: [{ where: { node: { name: { eq: "Tom Hanks" } } }, edge: { screenTime: 60 } }] } } ] @@ -158,7 +159,8 @@ describe("Relationship Properties Connect Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -173,7 +175,7 @@ describe("Relationship Properties Connect Cypher", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actors_connect0_node - MERGE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) + CREATE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) SET this0_actors_connect0_relationship.screenTime = $this0_actors_connect0_relationship_screenTime } } @@ -219,7 +221,7 @@ describe("Relationship Properties Connect Cypher", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Forrest Gump" } + where: { title: { eq: "Forrest Gump" } } update: { actors: { connect: { edge: { screenTime: 60 } } } } ) { movies { @@ -242,7 +244,8 @@ describe("Relationship Properties Connect Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { @@ -255,7 +258,7 @@ describe("Relationship Properties Connect Cypher", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) + CREATE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) SET this_actors0_connect0_relationship.screenTime = $this_actors0_connect0_relationship_screenTime } } @@ -295,9 +298,11 @@ describe("Relationship Properties Connect Cypher", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Forrest Gump" } + where: { title: { eq: "Forrest Gump" } } update: { - actors: { connect: { where: { node: { name_EQ: "Tom Hanks" } }, edge: { screenTime: 60 } } } + actors: { + connect: { where: { node: { name: { eq: "Tom Hanks" } } }, edge: { screenTime: 60 } } + } } ) { movies { @@ -320,7 +325,8 @@ describe("Relationship Properties Connect Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { @@ -334,7 +340,7 @@ describe("Relationship Properties Connect Cypher", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) + CREATE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) SET this_actors0_connect0_relationship.screenTime = $this_actors0_connect0_relationship_screenTime } } diff --git a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts index c9b33370d7..a20364e245 100644 --- a/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts +++ b/packages/graphql/tests/tck/connections/relationship_properties/create.test.ts @@ -77,7 +77,8 @@ describe("Relationship Properties Create Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) diff --git a/packages/graphql/tests/tck/connections/relationship_properties/update.test.ts b/packages/graphql/tests/tck/connections/relationship_properties/update.test.ts index 8d15186aae..c4a6e4dc9f 100644 --- a/packages/graphql/tests/tck/connections/relationship_properties/update.test.ts +++ b/packages/graphql/tests/tck/connections/relationship_properties/update.test.ts @@ -50,9 +50,11 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Forrest Gump" } + where: { title: { eq: "Forrest Gump" } } update: { - actors: [{ where: { node: { name_EQ: "Tom Hanks" } }, update: { edge: { screenTime: 60 } } }] + actors: [ + { where: { node: { name: { eq: "Tom Hanks" } } }, update: { edge: { screenTime_SET: 60 } } } + ] } ) { movies { @@ -65,14 +67,15 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { WITH this MATCH (this)<-[this_acted_in0_relationship:ACTED_IN]-(this_actors0:Actor) WHERE this_actors0.name = $updateMovies_args_update_actors0_where_this_actors0param0 - SET this_acted_in0_relationship.screenTime = $updateMovies.args.update.actors[0].update.edge.screenTime + SET this_acted_in0_relationship.screenTime = $updateMovies.args.update.actors[0].update.edge.screenTime_SET RETURN count(*) AS update_this_actors0 } RETURN collect(DISTINCT this { .title }) AS data" @@ -89,12 +92,14 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Tom Hanks\\" + \\"name\\": { + \\"eq\\": \\"Tom Hanks\\" + } } }, \\"update\\": { \\"edge\\": { - \\"screenTime\\": { + \\"screenTime_SET\\": { \\"low\\": 60, \\"high\\": 0 } @@ -114,11 +119,11 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Forrest Gump" } + where: { title: { eq: "Forrest Gump" } } update: { actors: [ { - where: { node: { name_EQ: "Tom Hanks" } } + where: { node: { name: { eq: "Tom Hanks" } } } update: { edge: { screenTime_SET: 60 }, node: { name_SET: "Tom Hanks" } } } ] @@ -134,7 +139,8 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { @@ -160,7 +166,9 @@ describe("Cypher -> Connections -> Relationship Properties -> Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Tom Hanks\\" + \\"name\\": { + \\"eq\\": \\"Tom Hanks\\" + } } }, \\"update\\": { diff --git a/packages/graphql/tests/tck/connections/sort.test.ts b/packages/graphql/tests/tck/connections/sort.test.ts index 7c24a37a5f..0258b4d91a 100644 --- a/packages/graphql/tests/tck/connections/sort.test.ts +++ b/packages/graphql/tests/tck/connections/sort.test.ts @@ -75,7 +75,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -136,7 +137,8 @@ describe("Relationship Properties Cypher", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { diff --git a/packages/graphql/tests/tck/connections/top-level-interfaces.test.ts b/packages/graphql/tests/tck/connections/top-level-interfaces.test.ts index 2942b73679..7164190fd5 100644 --- a/packages/graphql/tests/tck/connections/top-level-interfaces.test.ts +++ b/packages/graphql/tests/tck/connections/top-level-interfaces.test.ts @@ -62,7 +62,7 @@ describe("Top level interface connections", () => { test("Top level connection", async () => { const query = /* GraphQL */ ` query { - showsConnection(where: { title_EQ: "The Matrix" }) { + showsConnection(where: { title: { eq: "The Matrix" } }) { edges { node { title @@ -78,7 +78,8 @@ describe("Top level interface connections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:Movie) WHERE this0.title = $param0 WITH { node: { __resolveType: \\"Movie\\", __id: id(this0), cost: this0.cost, title: this0.title } } AS edge @@ -104,7 +105,7 @@ describe("Top level interface connections", () => { test("Top level connection with limit", async () => { const query = /* GraphQL */ ` query { - showsConnection(where: { title_EQ: "The Matrix" }, first: 2) { + showsConnection(where: { title: { eq: "The Matrix" } }, first: 2) { edges { node { title @@ -120,7 +121,8 @@ describe("Top level interface connections", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:Movie) WHERE this0.title = $param0 WITH { node: { __resolveType: \\"Movie\\", __id: id(this0), cost: this0.cost, title: this0.title } } AS edge diff --git a/packages/graphql/tests/tck/connections/unions.test.ts b/packages/graphql/tests/tck/connections/unions.test.ts index c80ad33554..99ec33cc35 100644 --- a/packages/graphql/tests/tck/connections/unions.test.ts +++ b/packages/graphql/tests/tck/connections/unions.test.ts @@ -80,7 +80,8 @@ describe("Cypher -> Connections -> Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -111,8 +112,8 @@ describe("Cypher -> Connections -> Unions", () => { name publicationsConnection( where: { - Book: { node: { title_EQ: "Book Title" } } - Journal: { node: { subject_EQ: "Journal Subject" } } + Book: { node: { title: { eq: "Book Title" } } } + Journal: { node: { subject: { eq: "Journal Subject" } } } } ) { edges { @@ -136,7 +137,8 @@ describe("Cypher -> Connections -> Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -173,7 +175,7 @@ describe("Cypher -> Connections -> Unions", () => { authors { name publicationsConnection( - where: { Book: { edge: { words_EQ: 1000 } }, Journal: { edge: { words_EQ: 2000 } } } + where: { Book: { edge: { words: { eq: 1000 } } }, Journal: { edge: { words: { eq: 2000 } } } } ) { edges { properties { @@ -196,7 +198,8 @@ describe("Cypher -> Connections -> Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -240,8 +243,8 @@ describe("Cypher -> Connections -> Unions", () => { name publicationsConnection( where: { - Book: { edge: { words_EQ: 1000 }, node: { title_EQ: "Book Title" } } - Journal: { edge: { words_EQ: 2000 }, node: { subject_EQ: "Journal Subject" } } + Book: { edge: { words: { eq: 1000 } }, node: { title: { eq: "Book Title" } } } + Journal: { edge: { words: { eq: 2000 } }, node: { subject: { eq: "Journal Subject" } } } } ) { edges { @@ -265,7 +268,8 @@ describe("Cypher -> Connections -> Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -331,7 +335,8 @@ describe("Cypher -> Connections -> Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/deprecated/cypher-sort-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/cypher-sort-deprecated.test.ts index 2dd481be43..42fe975db8 100644 --- a/packages/graphql/tests/tck/deprecated/cypher-sort-deprecated.test.ts +++ b/packages/graphql/tests/tck/deprecated/cypher-sort-deprecated.test.ts @@ -97,7 +97,7 @@ describe("Cypher sort deprecated", () => { test("with field in selection set", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ id: DESC }] }) { + movies(sort: [{ id: DESC }]) { id title } @@ -107,7 +107,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC RETURN this { .id, .title } AS this" @@ -119,7 +120,7 @@ describe("Cypher sort deprecated", () => { test("with field aliased in selection set", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ id: DESC }] }) { + movies(sort: [{ id: DESC }]) { aliased: id title } @@ -129,7 +130,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC RETURN this { .title, .id, aliased: this.id } AS this" @@ -141,7 +143,7 @@ describe("Cypher sort deprecated", () => { test("with field not in selection set", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ id: DESC }] }) { + movies(sort: [{ id: DESC }]) { title } } @@ -150,7 +152,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC RETURN this { .title, .id } AS this" @@ -163,7 +166,7 @@ describe("Cypher sort deprecated", () => { test("Simple Sort On Cypher Field Without Projection", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ totalGenres: DESC }] }) { + movies(sort: [{ totalGenres: DESC }]) { title } } @@ -172,7 +175,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -204,7 +208,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -221,11 +226,11 @@ describe("Cypher sort deprecated", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); }); - + test("Simple Sort On Cypher Field", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ totalGenres: DESC }] }) { + movies(sort: [{ totalGenres: DESC }]) { totalGenres } } @@ -234,7 +239,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -257,7 +263,7 @@ describe("Cypher sort deprecated", () => { test("Multi Sort", async () => { const query = /* GraphQL */ ` { - movies(options: { sort: [{ id: DESC }, { title: ASC }] }) { + movies(sort: [{ id: DESC }, { title: ASC }]) { id title } @@ -267,7 +273,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC, this.title ASC RETURN this { .id, .title } AS this" @@ -280,7 +287,9 @@ describe("Cypher sort deprecated", () => { const query = /* GraphQL */ ` query ($title: String, $offset: Int, $limit: Int) { movies( - options: { sort: [{ id: DESC }, { title: ASC }], offset: $offset, limit: $limit } + sort: [{ id: DESC }, { title: ASC }] + offset: $offset + limit: $limit where: { title_EQ: $title } ) { id @@ -294,7 +303,8 @@ describe("Cypher sort deprecated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * ORDER BY this.id DESC, this.title ASC @@ -322,7 +332,7 @@ describe("Cypher sort deprecated", () => { const query = /* GraphQL */ ` { movies { - genres(options: { sort: [{ name: DESC }] }) { + genres(sort: [{ name: DESC }]) { name } } @@ -332,10 +342,12 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 WITH this1 { .name } AS this1 ORDER BY this1.name DESC RETURN collect(this1) AS var2 @@ -350,7 +362,7 @@ describe("Cypher sort deprecated", () => { const query = /* GraphQL */ ` { movies { - genres(options: { sort: [{ name: ASC }] }) { + genres(sort: [{ name: ASC }]) { name } } @@ -360,10 +372,12 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 WITH this1 { .name } AS this1 ORDER BY this1.name ASC RETURN collect(this1) AS var2 @@ -378,7 +392,7 @@ describe("Cypher sort deprecated", () => { const query = /* GraphQL */ ` { movies { - genres(options: { sort: [{ totalMovies: ASC }] }) { + genres(sort: [{ totalMovies: ASC }]) { name totalMovies } @@ -389,10 +403,12 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 CALL { WITH this1 CALL { @@ -443,7 +459,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -533,7 +550,8 @@ describe("Cypher sort deprecated", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/advanced-filtering-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/advanced-filtering-deprecated.test.ts new file mode 100644 index 0000000000..48a00c2b5b --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/advanced-filtering-deprecated.test.ts @@ -0,0 +1,861 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Cypher Advanced Filtering - deprecated", () => { + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + type Movie @node { + _id: ID + id: ID + title: String + actorCount: Int + budget: BigInt + genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) + } + + type Genre @node { + name: String + movies: [Movie!]! @relationship(type: "IN_GENRE", direction: IN) + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { + filters: { + String: { + LT: true, + GT: true, + LTE: true, + GTE: true, + MATCHES: true, + }, + ID: { + MATCHES: true, + }, + }, + }, + }); + }); + + test("EQ", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_EQ: "The Matrix" }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.title = $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\" + }" + `); + }); + + test("IN", async () => { + const query = /* GraphQL */ ` + { + movies(where: { _id_IN: ["123"] }) { + _id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this._id IN $param0 + RETURN this { ._id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": [ + \\"123\\" + ] + }" + `); + }); + + test("REGEX", async () => { + const query = /* GraphQL */ ` + { + movies(where: { id_MATCHES: "(?i)123.*" }) { + id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id =~ $param0 + RETURN this { .id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"(?i)123.*\\" + }" + `); + }); + + test("NOT", async () => { + const query = /* GraphQL */ ` + { + movies(where: { NOT: { id_EQ: "123" } }) { + id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE NOT (this.id = $param0) + RETURN this { .id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\" + }" + `); + }); + + test("CONTAINS", async () => { + const query = /* GraphQL */ ` + { + movies(where: { id_CONTAINS: "123" }) { + id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id CONTAINS $param0 + RETURN this { .id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\" + }" + `); + }); + + test("STARTS_WITH", async () => { + const query = /* GraphQL */ ` + { + movies(where: { id_STARTS_WITH: "123" }) { + id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id STARTS WITH $param0 + RETURN this { .id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\" + }" + `); + }); + + test("ENDS_WITH", async () => { + const query = /* GraphQL */ ` + { + movies(where: { id_ENDS_WITH: "123" }) { + id + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id ENDS WITH $param0 + RETURN this { .id } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\" + }" + `); + }); + + test("LT", async () => { + const query = /* GraphQL */ ` + { + movies(where: { actorCount_LT: 123 }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.actorCount < $param0 + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 123, + \\"high\\": 0 + } + }" + `); + }); + + test("LT BigInt", async () => { + const query = /* GraphQL */ ` + { + movies(where: { budget_LT: 9223372036854775807 }) { + budget + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.budget < $param0 + RETURN this { .budget } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": -1, + \\"high\\": 2147483647 + } + }" + `); + }); + + test("LT String", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_LT: "The Matrix Revolutions" }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.title < $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix Revolutions\\" + }" + `); + }); + + test("LTE", async () => { + const query = /* GraphQL */ ` + { + movies(where: { actorCount_LTE: 123 }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.actorCount <= $param0 + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 123, + \\"high\\": 0 + } + }" + `); + }); + + test("LTE BigInt", async () => { + const query = /* GraphQL */ ` + { + movies(where: { budget_LTE: 9223372036854775807 }) { + budget + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.budget <= $param0 + RETURN this { .budget } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": -1, + \\"high\\": 2147483647 + } + }" + `); + }); + + test("LTE String", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_LTE: "The Matrix Revolutions" }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.title <= $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix Revolutions\\" + }" + `); + }); + + test("GT", async () => { + const query = /* GraphQL */ ` + { + movies(where: { actorCount_GT: 123 }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.actorCount > $param0 + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 123, + \\"high\\": 0 + } + }" + `); + }); + + test("GT BigInt", async () => { + const query = /* GraphQL */ ` + { + movies(where: { budget_GT: 9223372036854775000 }) { + budget + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.budget > $param0 + RETURN this { .budget } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": -808, + \\"high\\": 2147483647 + } + }" + `); + }); + + test("GT String", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_GT: "The Matrix Revolutions" }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.title > $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix Revolutions\\" + }" + `); + }); + + test("GTE", async () => { + const query = /* GraphQL */ ` + { + movies(where: { actorCount_GTE: 123 }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.actorCount >= $param0 + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 123, + \\"high\\": 0 + } + }" + `); + }); + + test("GTE BigInt", async () => { + const query = /* GraphQL */ ` + { + movies(where: { budget_GTE: 9223372036854775000 }) { + budget + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.budget >= $param0 + RETURN this { .budget } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": -808, + \\"high\\": 2147483647 + } + }" + `); + }); + + test("GTE String", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_GTE: "The Matrix Revolutions" }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.title >= $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix Revolutions\\" + }" + `); + }); + + describe("Relationships", () => { + test("equality", async () => { + const query = /* GraphQL */ ` + { + movies(where: { genres_SOME: { name_EQ: "some genre" } }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE this0.name = $param0 + } + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + + test("NONE", async () => { + const query = /* GraphQL */ ` + { + movies(where: { genres_NONE: { name_EQ: "some genre" } }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE NOT (EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE this0.name = $param0 + }) + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + + describe("List Predicates", () => { + const generateQuery = (operator: "ALL" | "NONE" | "SINGLE" | "SOME"): string => { + const query = /* GraphQL */ ` + { + movies(where: { genres_${operator}: { name_EQ: "some genre" } }) { + actorCount + } + } + `; + return query; + }; + test("ALL", async () => { + const query = generateQuery("ALL"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE (EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE this0.name = $param0 + } AND NOT (EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE NOT (this0.name = $param0) + })) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("NONE", async () => { + const query = generateQuery("NONE"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE NOT (EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE this0.name = $param0 + }) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("SINGLE", async () => { + const query = generateQuery("SINGLE"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE single(this0 IN [(this)-[:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 | 1] WHERE true) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("SOME", async () => { + const query = generateQuery("SOME"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE EXISTS { + MATCH (this)-[:IN_GENRE]->(this0:Genre) + WHERE this0.name = $param0 + } + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + }); + }); + + describe("Connections", () => { + test("Node and relationship properties equality", async () => { + const query = /* GraphQL */ ` + { + movies(where: { genresConnection_SOME: { node: { name_EQ: "some genre" } } }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE this1.name = $param0 + } + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + + test("Node and relationship properties NONE", async () => { + const query = /* GraphQL */ ` + { + movies(where: { genresConnection_NONE: { node: { name_EQ: "some genre" } } }) { + actorCount + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE NOT (EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE this1.name = $param0 + }) + RETURN this { .actorCount } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + + describe("List Predicates", () => { + const generateQuery = (operator: "ALL" | "NONE" | "SINGLE" | "SOME"): string => { + const query = /* GraphQL */ ` + { + movies(where: { genresConnection_${operator}: { node: { name_EQ: "some genre" } } }) { + actorCount + } + } + `; + return query; + }; + test("ALL", async () => { + const query = generateQuery("ALL"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE (EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE this1.name = $param0 + } AND NOT (EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE NOT (this1.name = $param0) + })) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("NONE", async () => { + const query = generateQuery("NONE"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE NOT (EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE this1.name = $param0 + }) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("SINGLE", async () => { + const query = generateQuery("SINGLE"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE single(this0 IN [(this)-[this1:IN_GENRE]->(this0:Genre) WHERE this0.name = $param0 | 1] WHERE true) + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + test("SOME", async () => { + const query = generateQuery("SOME"); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE EXISTS { + MATCH (this)-[this0:IN_GENRE]->(this1:Genre) + WHERE this1.name = $param0 + } + RETURN this { .actorCount } AS this" + `); + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some genre\\" + }" + `); + }); + }); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-list-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-list-deprecated.test.ts new file mode 100644 index 0000000000..a27a6e3b1b --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-list-deprecated.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("cypher directive filtering - Lists - deprecated", () => { + test("Int cypher field AND String title field", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + custom_cypher_list: [String] + @cypher( + statement: """ + RETURN ['a', 'b', 'c'] as list + """ + columnName: "list" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { custom_cypher_list_INCLUDES: "a" }) { + title + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + RETURN ['a', 'b', 'c'] as list + } + UNWIND list AS var0 + WITH var0 AS this1 + RETURN collect(this1) AS var2 + } + WITH * + WHERE $param0 IN var2 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"a\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-one-to-one-relationship-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-one-to-one-relationship-deprecated.test.ts new file mode 100644 index 0000000000..b392527680 --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-one-to-one-relationship-deprecated.test.ts @@ -0,0 +1,1608 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("cypher directive filtering - One To One Relationship - deprecated", () => { + test("1 to 1 relationship", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actor: Actor! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(actor:Actor) + RETURN actor + """ + columnName: "actor" + ) + } + + type Actor @node { + name: String + movie: Movie! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { actor: { name_EQ: "Keanu Reeves" } }) { + title + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:ACTED_IN]->(actor:Actor) + RETURN actor + } + WITH actor AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.name = $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu Reeves\\" + }" + `); + }); + + test("1 to 1 relationship with multiple filters", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actor: Actor! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(actor:Actor) + RETURN actor + """ + columnName: "actor" + ) + } + + type Actor @node { + name: String + age: Int + movie: Movie! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { released_EQ: 2003, actor: { name_EQ: "Keanu Reeves", age_GT: 30 } }) { + title + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:ACTED_IN]->(actor:Actor) + RETURN actor + } + WITH actor AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE (this.released = $param0 AND (this1.name = $param1 AND this1.age > $param2)) + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 2003, + \\"high\\": 0 + }, + \\"param1\\": \\"Keanu Reeves\\", + \\"param2\\": { + \\"low\\": 30, + \\"high\\": 0 + } + }" + `); + }); + + test("1 to 1 relationship with single property filter", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + actor: Actor! + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + """ + columnName: "actor" + ) + } + + type Actor @node { + name: String + movie: Movie! + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const query = /* GraphQL */ ` + query { + movies(where: { actor: { name_EQ: "Keanu Reeves" } }) { + title + actor { + name + } + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + } + WITH actor AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.name = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + } + WITH actor AS this2 + WITH this2 { .name } AS this2 + RETURN head(collect(this2)) AS var3 + } + RETURN this { .title, actor: var3 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu Reeves\\" + }" + `); + }); + + test("1 to 1 relationship with null filter", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actor: Actor + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + """ + columnName: "actor" + ) + } + + type Actor @node { + name: String + movie: Movie + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const query = /* GraphQL */ ` + query { + movies(where: { released_EQ: 2003, actor: null }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + } + WITH actor AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE (this.released = $param0 AND this1 IS NULL) + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 2003, + \\"high\\": 0 + } + }" + `); + }); + + test("1 to 1 relationship with NOT null filter", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actor: Actor + @cypher( + statement: """ + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + """ + columnName: "actor" + ) + } + + type Actor @node { + name: String + movie: Movie + @cypher( + statement: """ + MATCH (this)-[:ACTED_IN]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const query = /* GraphQL */ ` + query { + movies(where: { AND: [{ released_IN: [2003], NOT: { actor: null } }] }) { + title + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)<-[:ACTED_IN]-(actor:Actor) + RETURN actor + } + WITH actor AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE (this.released IN $param0 AND NOT (this1 IS NULL)) + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": [ + { + \\"low\\": 2003, + \\"high\\": 0 + } + ] + }" + `); + }); + + test("1 to 1 relationship with auth filter on type PASS", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Lilly Wachowski\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth filter on type FAIL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Something Incorrect" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Something Incorrect\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth filter on field PASS", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + directed_by: Person! + @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Lilly Wachowski\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth filter on field FAIL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + directed_by: Person! + @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Something Incorrect" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Something Incorrect\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth validate type PASS", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Lilly Wachowski\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth validate type FAIL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + title: String + released: Int + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Something Wrong" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Something Wrong\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth validate field PASS", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + directed_by: Person! + @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Lilly Wachowski" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Lilly Wachowski\\" + } + }" + `); + }); + + test("1 to 1 relationship with auth validate field FAIL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + directed_by: Person! + @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const token = createBearerToken("secret", { custom_value: "Something Wrong" }); + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: "secret", + }, + }, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query, { token }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this3 + WITH this3 { .name } AS this3 + RETURN head(collect(this3)) AS var4 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this5 + RETURN head(collect(this5)) AS this6 + } + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.custom_value IS NOT NULL AND this6.name = $jwt.custom_value)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this2 { .title, directed_by: var4 } AS this2 + RETURN head(collect(this2)) AS var7 + } + RETURN this { directed: var7 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [], + \\"custom_value\\": \\"Something Wrong\\" + } + }" + `); + }); + + test("1 to 1 relationship with nested selection", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN) + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const query = /* GraphQL */ ` + query { + people(where: { directed: { title_EQ: "The Matrix" } }) { + directed { + title + directed_by { + name + } + actors { + name + movies { + directed_by { + name + } + title + } + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Person) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE this1.title = $param0 + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + } + WITH movie AS this2 + CALL { + WITH this2 + MATCH (this2)<-[this3:ACTED_IN]-(this4:Person) + WITH DISTINCT this4 + CALL { + WITH this4 + MATCH (this4)-[this5:ACTED_IN]->(this6:Movie) + WITH DISTINCT this6 + CALL { + WITH this6 + CALL { + WITH this6 + WITH this6 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this7 + WITH this7 { .name } AS this7 + RETURN head(collect(this7)) AS var8 + } + WITH this6 { .title, directed_by: var8 } AS this6 + RETURN collect(this6) AS var9 + } + WITH this4 { .name, movies: var9 } AS this4 + RETURN collect(this4) AS var10 + } + CALL { + WITH this2 + CALL { + WITH this2 + WITH this2 AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this11 + WITH this11 { .name } AS this11 + RETURN head(collect(this11)) AS var12 + } + WITH this2 { .title, directed_by: var12, actors: var10 } AS this2 + RETURN head(collect(this2)) AS var13 + } + RETURN this { directed: var13 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"The Matrix\\" + }" + `); + }); + + test("1 to 1 relationship with connection", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + released: Int + actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN) + directed_by: Person! + @cypher( + statement: """ + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + """ + columnName: "director" + ) + } + + type Person @node { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) + directed: Movie! + @cypher( + statement: """ + MATCH (this)-[:DIRECTED]->(movie:Movie) + RETURN movie + """ + columnName: "movie" + ) + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const query = /* GraphQL */ ` + query { + movies(where: { directed_by: { name_EQ: "Lilly Wachowski" }, title_ENDS_WITH: "Matrix" }) { + actorsConnection { + totalCount + edges { + node { + name + } + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)<-[:DIRECTED]-(director:Person) + RETURN director + } + WITH director AS this0 + RETURN head(collect(this0)) AS this1 + } + WITH * + WHERE (this.title ENDS WITH $param0 AND this1.name = $param1) + CALL { + WITH this + MATCH (this)<-[this2:ACTED_IN]-(this3:Person) + WITH collect({ node: this3, relationship: this2 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this3, edge.relationship AS this2 + RETURN collect({ node: { name: this3.name, __resolveType: \\"Person\\" } }) AS var4 + } + RETURN { edges: var4, totalCount: totalCount } AS var5 + } + RETURN this { actorsConnection: var5 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Matrix\\", + \\"param1\\": \\"Lilly Wachowski\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-scalar-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-scalar-deprecated.test.ts new file mode 100644 index 0000000000..cdcd392e44 --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/cypher-filtering-scalar-deprecated.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("cypher directive filtering - deprecated", () => { + test("Int cypher field AND String title field", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:Movie) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { special_count_GTE: 1, title_EQ: "CustomType One" }) { + special_count + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (m:Movie) + RETURN count(m) as c + } + WITH c AS this0 + RETURN this0 AS var1 + } + WITH * + WHERE (this.title = $param0 AND var1 >= $param1) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (m:Movie) + RETURN count(m) as c + } + WITH c AS this2 + RETURN this2 AS var3 + } + RETURN this { special_count: var3 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"CustomType One\\", + \\"param1\\": { + \\"low\\": 1, + \\"high\\": 0 + } + }" + `); + }); + + test("unmatched Int cypher field AND String title field", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:Movie) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { special_count_GTE: 1, title_EQ: "CustomType Unknown" }) { + special_count + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (m:Movie) + RETURN count(m) as c + } + WITH c AS this0 + RETURN this0 AS var1 + } + WITH * + WHERE (this.title = $param0 AND var1 >= $param1) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (m:Movie) + RETURN count(m) as c + } + WITH c AS this2 + RETURN this2 AS var3 + } + RETURN this { special_count: var3 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"CustomType Unknown\\", + \\"param1\\": { + \\"low\\": 1, + \\"high\\": 0 + } + }" + `); + }); + + test("Int cypher field, selecting String title field", async () => { + const typeDefs = /* GraphQL */ ` + type Movie @node { + title: String + special_count: Int + @cypher( + statement: """ + MATCH (m:Movie) + RETURN count(m) as c + """ + columnName: "c" + ) + } + `; + + const query = /* GraphQL */ ` + query { + movies(where: { special_count_GTE: 1 }) { + title + } + } + `; + + const neoSchema: Neo4jGraphQL = new Neo4jGraphQL({ + typeDefs, + }); + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (m:Movie) + RETURN count(m) as c + } + WITH c AS this0 + RETURN this0 AS var1 + } + WITH * + WHERE var1 >= $param0 + RETURN this { .title } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": { + \\"low\\": 1, + \\"high\\": 0 + } + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/delete-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/delete-deprecated.test.ts new file mode 100644 index 0000000000..8f5e37027a --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/delete-deprecated.test.ts @@ -0,0 +1,309 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Cypher Delete - Deprecated", () => { + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + type Actor @node { + name: String + movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) + } + + type Movie @node { + id: ID + title: String + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + }); + + test("Simple Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteMovies(where: { id_EQ: "123" }) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id = $param0 + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\" + }" + `); + }); + + test("Single Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteMovies( + where: { id_EQ: 123 } + delete: { actors: { where: { node: { name_EQ: "Actor to delete" } } } } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) + WHERE this1.name = $param1 + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\", + \\"param1\\": \\"Actor to delete\\" + }" + `); + }); + + test("Single Nested Delete deleting multiple", async () => { + const query = /* GraphQL */ ` + mutation { + deleteMovies( + where: { id_EQ: 123 } + delete: { + actors: [ + { where: { node: { name_EQ: "Actor to delete" } } } + { where: { node: { name_EQ: "Another actor to delete" } } } + ] + } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) + WHERE this1.name = $param1 + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + CALL { + WITH * + OPTIONAL MATCH (this)<-[this4:ACTED_IN]-(this5:Actor) + WHERE this5.name = $param2 + WITH this4, collect(DISTINCT this5) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\", + \\"param1\\": \\"Actor to delete\\", + \\"param2\\": \\"Another actor to delete\\" + }" + `); + }); + + test("Double Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteMovies( + where: { id_EQ: 123 } + delete: { + actors: { + where: { node: { name_EQ: "Actor to delete" } } + delete: { movies: { where: { node: { id_EQ: 321 } } } } + } + } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) + WHERE this1.name = $param1 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) + WHERE this3.id = $param2 + WITH this2, collect(DISTINCT this3) AS var4 + CALL { + WITH var4 + UNWIND var4 AS var5 + DETACH DELETE var5 + } + } + WITH this0, collect(DISTINCT this1) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\", + \\"param1\\": \\"Actor to delete\\", + \\"param2\\": \\"321\\" + }" + `); + }); + + test("Triple Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteMovies( + where: { id_EQ: 123 } + delete: { + actors: { + where: { node: { name_EQ: "Actor to delete" } } + delete: { + movies: { + where: { node: { id_EQ: 321 } } + delete: { actors: { where: { node: { name_EQ: "Another actor to delete" } } } } + } + } + } + } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Movie) + WHERE this.id = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) + WHERE this1.name = $param1 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) + WHERE this3.id = $param2 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this3)<-[this4:ACTED_IN]-(this5:Actor) + WHERE this5.name = $param3 + WITH this4, collect(DISTINCT this5) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH this2, collect(DISTINCT this3) AS var8 + CALL { + WITH var8 + UNWIND var8 AS var9 + DETACH DELETE var9 + } + } + WITH this0, collect(DISTINCT this1) AS var10 + CALL { + WITH var10 + UNWIND var10 AS var11 + DETACH DELETE var11 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"123\\", + \\"param1\\": \\"Actor to delete\\", + \\"param2\\": \\"321\\", + \\"param3\\": \\"Another actor to delete\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/delete-interface-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/delete-interface-deprecated.test.ts new file mode 100644 index 0000000000..b2e01e71af --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/delete-interface-deprecated.test.ts @@ -0,0 +1,355 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Cypher Delete - interface - deprecated", () => { + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + type Episode @node { + runtime: Int! + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) + } + + interface Production { + title: String! + actors: [Actor!]! @declareRelationship + } + + interface Worker { + name: String + } + + type ScreenWriter implements Worker @node { + name: String + } + + type StuntPerformer implements Worker @node { + name: String! + workedOn: [Production!]! @relationship(type: "WORKED_ON", direction: OUT) + } + + type Movie implements Production @node { + title: String! + runtime: Int! + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + workers: [Worker!]! @relationship(type: "WORKED_ON", direction: IN) + } + + type Series implements Production @node { + title: String! + episodes: [Episode!]! @relationship(type: "HAS_EPISODE", direction: OUT) + actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") + } + + type Actor @node { + name: String! + actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") + } + + type ActedIn @relationshipProperties { + screenTime: Int! + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + }); + + test("Simple Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteActors(where: { name_EQ: "Keanu" }) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Actor) + WHERE this.name = $param0 + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu\\" + }" + `); + }); + + test("Single Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteActors( + where: { name_EQ: "Keanu" } + delete: { actedIn: { where: { node: { title_EQ: "Matrix" } } } } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Actor) + WHERE this.name = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WHERE this1.title = $param1 + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + CALL { + WITH * + OPTIONAL MATCH (this)-[this4:ACTED_IN]->(this5:Series) + WHERE this5.title = $param2 + WITH this4, collect(DISTINCT this5) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu\\", + \\"param1\\": \\"Matrix\\", + \\"param2\\": \\"Matrix\\" + }" + `); + }); + + test("Single Nested Delete, + typename filter", async () => { + const query = /* GraphQL */ ` + mutation { + deleteActors( + where: { name_EQ: "Keanu" } + delete: { actedIn: { where: { node: { typename: [Movie], title_EQ: "Matrix" } } } } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Actor) + WHERE this.name = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WHERE (this1.title = $param1 AND this1:Movie) + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + CALL { + WITH * + OPTIONAL MATCH (this)-[this4:ACTED_IN]->(this5:Series) + WHERE (this5.title = $param2 AND this5:Movie) + WITH this4, collect(DISTINCT this5) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu\\", + \\"param1\\": \\"Matrix\\", + \\"param2\\": \\"Matrix\\" + }" + `); + }); + + test("Single Nested Delete, deleting multiple", async () => { + const query = /* GraphQL */ ` + mutation { + deleteActors( + where: { name_EQ: "Keanu" } + delete: { + actedIn: { where: { node: { OR: [{ title_EQ: "Matrix" }, { title_EQ: "Matrix Reloaded" }] } } } + } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Actor) + WHERE this.name = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WHERE (this1.title = $param1 OR this1.title = $param2) + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + CALL { + WITH * + OPTIONAL MATCH (this)-[this4:ACTED_IN]->(this5:Series) + WHERE (this5.title = $param3 OR this5.title = $param4) + WITH this4, collect(DISTINCT this5) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu\\", + \\"param1\\": \\"Matrix\\", + \\"param2\\": \\"Matrix Reloaded\\", + \\"param3\\": \\"Matrix\\", + \\"param4\\": \\"Matrix Reloaded\\" + }" + `); + }); + + test("Double Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteActors( + where: { name_EQ: "Keanu" } + delete: { + actedIn: { + where: { node: { title_EQ: "Matrix" } } + delete: { actors: { where: { node: { name_EQ: "Gloria Foster" } } } } + } + } + ) { + nodesDeleted + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Actor) + WHERE this.name = $param0 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WHERE this1.title = $param1 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this1)<-[this2:ACTED_IN]-(this3:Actor) + WHERE this3.name = $param2 + WITH this2, collect(DISTINCT this3) AS var4 + CALL { + WITH var4 + UNWIND var4 AS var5 + DETACH DELETE var5 + } + } + WITH this0, collect(DISTINCT this1) AS var6 + CALL { + WITH var6 + UNWIND var6 AS var7 + DETACH DELETE var7 + } + } + CALL { + WITH * + OPTIONAL MATCH (this)-[this8:ACTED_IN]->(this9:Series) + WHERE this9.title = $param3 + WITH * + CALL { + WITH * + OPTIONAL MATCH (this9)<-[this10:ACTED_IN]-(this11:Actor) + WHERE this11.name = $param4 + WITH this10, collect(DISTINCT this11) AS var12 + CALL { + WITH var12 + UNWIND var12 AS var13 + DETACH DELETE var13 + } + } + WITH this8, collect(DISTINCT this9) AS var14 + CALL { + WITH var14 + UNWIND var14 AS var15 + DETACH DELETE var15 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"Keanu\\", + \\"param1\\": \\"Matrix\\", + \\"param2\\": \\"Gloria Foster\\", + \\"param3\\": \\"Matrix\\", + \\"param4\\": \\"Gloria Foster\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/node-label-interface-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/node-label-interface-deprecated.test.ts new file mode 100644 index 0000000000..d1a4729f40 --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/node-label-interface-deprecated.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Node directive with interface - deprecated", () => { + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + interface Search { + name: String + } + + type Genre implements Search @node(labels: ["Category", "ExtraLabel1", "ExtraLabel2"]) { + name: String + } + + type Movie implements Search @node(labels: ["Film"]) { + name: String + title: String + search: [Search!]! @relationship(type: "SEARCH", direction: OUT) + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + }); + }); + + test("Read Interface", async () => { + const query = /* GraphQL */ ` + { + movies(where: { title_EQ: "some title" }) { + search(where: { name_EQ: "Horror" }, offset: 1, limit: 10) { + ... on Movie { + title + } + ... on Genre { + name + } + } + } + } + `; + + const result = await translateQuery(neoSchema, query); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Film) + WHERE this.title = $param0 + CALL { + WITH this + CALL { + WITH * + MATCH (this)-[this0:SEARCH]->(this1:Category:ExtraLabel1:ExtraLabel2) + WHERE this1.name = $param1 + WITH this1 { .name, __resolveType: \\"Genre\\", __id: id(this1) } AS this1 + RETURN this1 AS var2 + UNION + WITH * + MATCH (this)-[this3:SEARCH]->(this4:Film) + WHERE this4.name = $param2 + WITH this4 { .title, __resolveType: \\"Movie\\", __id: id(this4) } AS this4 + RETURN this4 AS var2 + } + WITH var2 + SKIP $param3 + LIMIT $param4 + RETURN collect(var2) AS var2 + } + RETURN this { search: var2 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"param0\\": \\"some title\\", + \\"param1\\": \\"Horror\\", + \\"param2\\": \\"Horror\\", + \\"param3\\": { + \\"low\\": 1, + \\"high\\": 0 + }, + \\"param4\\": { + \\"low\\": 10, + \\"high\\": 0 + } + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/projection-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/projection-deprecated.test.ts new file mode 100644 index 0000000000..6b196034c8 --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/projection-deprecated.test.ts @@ -0,0 +1,135 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Cypher Auth Projection - deprecated", () => { + const secret = "secret"; + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + type User @node { + id: ID + name: String + } + + extend type User { + id: ID @authorization(validate: [{ when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } }]) + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { + authorization: { + key: secret, + }, + }, + }); + }); + + test("Update Node", async () => { + const query = /* GraphQL */ ` + mutation { + updateUsers(update: { id_SET: "new-id" }) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + SET this.id = $this_update_id_SET + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(DISTINCT this { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"this_update_id_SET\\": \\"new-id\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Create Node", async () => { + const query = /* GraphQL */ ` + mutation { + createUsers(input: [{ id: "id-1" }, { id: "id-2" }]) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + UNWIND $create_param0 AS create_var0 + CALL { + WITH create_var0 + CREATE (create_this1:User) + SET + create_this1.id = create_var0.id + RETURN create_this1 + } + RETURN collect(create_this1 { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"create_param0\\": [ + { + \\"id\\": \\"id-1\\" + }, + { + \\"id\\": \\"id-2\\" + } + ] + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/generic-filtering/roles-deprecated.test.ts b/packages/graphql/tests/tck/deprecated/generic-filtering/roles-deprecated.test.ts new file mode 100644 index 0000000000..f778e3bacf --- /dev/null +++ b/packages/graphql/tests/tck/deprecated/generic-filtering/roles-deprecated.test.ts @@ -0,0 +1,810 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Neo4jGraphQL } from "../../../../src"; +import { createBearerToken } from "../../../utils/create-bearer-token"; +import { formatCypher, formatParams, translateQuery } from "../../utils/tck-test-utils"; + +describe("Cypher Auth Roles - deprecated", () => { + const secret = "secret"; + let typeDefs: string; + let neoSchema: Neo4jGraphQL; + + beforeAll(() => { + typeDefs = /* GraphQL */ ` + type JWTPayload @jwt { + roles: [String!]! + } + + type History @node { + url: String + @authorization( + validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "super-admin" } } }] + ) + } + + type Comment @node { + id: String + content: String + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) + } + + type Post @node { + id: String + content: String + creator: [User!]! @relationship(type: "HAS_POST", direction: OUT) + comments: [Comment!]! @relationship(type: "HAS_COMMENT", direction: OUT) + } + + type User @node { + id: ID + name: String + password: String + posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) + } + + extend type User @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) + + extend type Post + @authorization( + validate: [ + { + operations: [CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, DELETE] + where: { jwt: { roles_INCLUDES: "super-admin" } } + } + ] + ) + + extend type User { + password: String + @authorization( + validate: [ + { operations: [READ, CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "super-admin" } } } + ] + ) + } + + extend type User { + history: [History] + @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:History) RETURN h", columnName: "h") + @authorization( + validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "super-admin" } } }] + ) + } + `; + + neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); + }); + + test("Read Node", async () => { + const query = /* GraphQL */ ` + { + users { + id + name + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN this { .id, .name } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"param2\\": \\"admin\\" + }" + `); + }); + + test("Read Node & Field", async () => { + const query = /* GraphQL */ ` + { + users { + id + name + password + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN this { .id, .name, .password } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"param2\\": \\"admin\\", + \\"param3\\": \\"super-admin\\" + }" + `); + }); + + test("Read Node & Cypher Field", async () => { + const query = /* GraphQL */ ` + { + users { + history { + url + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + CALL { + WITH this + CALL { + WITH this + WITH this AS this + MATCH (this)-[:HAS_HISTORY]->(h:History) RETURN h + } + WITH h AS this0 + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this0 { .url } AS this0 + RETURN collect(this0) AS var1 + } + RETURN this { history: var1 } AS this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"param2\\": \\"admin\\", + \\"param3\\": \\"super-admin\\", + \\"param4\\": \\"super-admin\\" + }" + `); + }); + + test("Create Node", async () => { + const query = /* GraphQL */ ` + mutation { + createUsers(input: [{ id: "1" }]) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + UNWIND $create_param0 AS create_var0 + CALL { + WITH create_var0 + CREATE (create_this1:User) + SET + create_this1.id = create_var0.id + WITH * + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate((create_var0.password IS NOT NULL AND NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param4 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN create_this1 + } + RETURN collect(create_this1 { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"create_param0\\": [ + { + \\"id\\": \\"1\\" + } + ], + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"create_param3\\": \\"admin\\", + \\"create_param4\\": \\"super-admin\\" + }" + `); + }); + + test("Create Node & Field", async () => { + const query = /* GraphQL */ ` + mutation { + createUsers(input: [{ id: "1", password: "super-password" }]) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + UNWIND $create_param0 AS create_var0 + CALL { + WITH create_var0 + CREATE (create_this1:User) + SET + create_this1.id = create_var0.id, + create_this1.password = create_var0.password + WITH * + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate((create_var0.password IS NOT NULL AND NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param4 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN create_this1 + } + RETURN collect(create_this1 { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"create_param0\\": [ + { + \\"id\\": \\"1\\", + \\"password\\": \\"super-password\\" + } + ], + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"create_param3\\": \\"admin\\", + \\"create_param4\\": \\"super-admin\\" + }" + `); + }); + + test("Update Node", async () => { + const query = /* GraphQL */ ` + mutation { + updateUsers(where: { id_EQ: "1" }, update: { id_SET: "id-1" }) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + SET this.id = $this_update_id_SET + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(DISTINCT this { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"update_param2\\": \\"admin\\", + \\"param0\\": \\"1\\", + \\"param3\\": \\"admin\\", + \\"this_update_id_SET\\": \\"id-1\\", + \\"authorization__after_param2\\": \\"admin\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Update Node & Field", async () => { + const query = /* GraphQL */ ` + mutation { + updateUsers(where: { id_EQ: "1" }, update: { password_SET: "password" }) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + SET this.password = $this_update_password_SET + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(DISTINCT this { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"update_param2\\": \\"admin\\", + \\"param0\\": \\"1\\", + \\"param3\\": \\"admin\\", + \\"this_update_password_SET\\": \\"password\\", + \\"authorization__before_param2\\": \\"super-admin\\", + \\"authorization__after_param2\\": \\"admin\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Connect", async () => { + const query = /* GraphQL */ ` + mutation { + updateUsers(update: { posts: { connect: {} } }) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + CALL { + WITH this + OPTIONAL MATCH (this_posts0_connect0_node:Post) + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + CALL { + WITH * + WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes + CALL { + WITH connectedNodes, parentNodes + UNWIND parentNodes as this + UNWIND connectedNodes as this_posts0_connect0_node + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) + } + } + WITH this, this_posts0_connect0_node + WITH this, this_posts0_connect0_node + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN count(*) AS connect_this_posts0_connect_Post0 + } + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(DISTINCT this { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"update_param2\\": \\"admin\\", + \\"param2\\": \\"admin\\", + \\"authorization__before_param2\\": \\"super-admin\\", + \\"authorization__before_param3\\": \\"admin\\", + \\"authorization__after_param2\\": \\"admin\\", + \\"authorization__after_param3\\": \\"super-admin\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Nested Connect", async () => { + const query = /* GraphQL */ ` + mutation { + updateComments( + update: { + post: { update: { node: { creator: { connect: { where: { node: { id_EQ: "user-id" } } } } } } } + } + ) { + comments { + content + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Comment) + WITH this + CALL { + WITH this + MATCH (this)<-[this_has_comment0_relationship:HAS_COMMENT]-(this_post0:Post) + WITH * + CALL { + WITH this, this_post0 + OPTIONAL MATCH (this_post0_creator0_connect0_node:User) + WHERE this_post0_creator0_connect0_node.id = $this_post0_creator0_connect0_node_param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + CALL { + WITH * + WITH this, collect(this_post0_creator0_connect0_node) as connectedNodes, collect(this_post0) as parentNodes + CALL { + WITH connectedNodes, parentNodes + UNWIND parentNodes as this_post0 + UNWIND connectedNodes as this_post0_creator0_connect0_node + CREATE (this_post0)-[:HAS_POST]->(this_post0_creator0_connect0_node) + } + } + WITH this, this_post0, this_post0_creator0_connect0_node + WITH this, this_post0, this_post0_creator0_connect0_node + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN count(*) AS connect_this_post0_creator0_connect_User0 + } + RETURN count(*) AS update_this_post0 + } + RETURN collect(DISTINCT this { .content }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"this_post0_creator0_connect0_node_param0\\": \\"user-id\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"authorization__before_param2\\": \\"admin\\", + \\"authorization__before_param3\\": \\"super-admin\\", + \\"authorization__after_param2\\": \\"super-admin\\", + \\"authorization__after_param3\\": \\"admin\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Disconnect", async () => { + const query = /* GraphQL */ ` + mutation { + updateUsers(update: { posts: { disconnect: {} } }) { + users { + id + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this + CALL { + WITH this + OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + CALL { + WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this + WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this + UNWIND this_posts0_disconnect0 as x + DELETE this_posts0_disconnect0_rel + } + WITH this, this_posts0_disconnect0 + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN count(*) AS disconnect_this_posts0_disconnect_Post + } + WITH this + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(DISTINCT this { .id }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"update_param2\\": \\"admin\\", + \\"param2\\": \\"admin\\", + \\"authorization__before_param2\\": \\"admin\\", + \\"authorization__before_param3\\": \\"super-admin\\", + \\"authorization__after_param2\\": \\"admin\\", + \\"authorization__after_param3\\": \\"super-admin\\", + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Nested Disconnect", async () => { + const query = /* GraphQL */ ` + mutation { + updateComments( + update: { + post: { + update: { node: { creator: { disconnect: { where: { node: { id_EQ: "user-id" } } } } } } + } + } + ) { + comments { + content + } + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:Comment) + WITH this + CALL { + WITH this + MATCH (this)<-[this_has_comment0_relationship:HAS_COMMENT]-(this_post0:Post) + WITH this, this_post0 + CALL { + WITH this, this_post0 + OPTIONAL MATCH (this_post0)-[this_post0_creator0_disconnect0_rel:HAS_POST]->(this_post0_creator0_disconnect0:User) + WHERE this_post0_creator0_disconnect0.id = $updateComments_args_update_post0_update_node_creator0_disconnect0_where_User_this_post0_creator0_disconnect0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + CALL { + WITH this_post0_creator0_disconnect0, this_post0_creator0_disconnect0_rel, this_post0 + WITH collect(this_post0_creator0_disconnect0) as this_post0_creator0_disconnect0, this_post0_creator0_disconnect0_rel, this_post0 + UNWIND this_post0_creator0_disconnect0 as x + DELETE this_post0_creator0_disconnect0_rel + } + WITH this, this_post0, this_post0_creator0_disconnect0 + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + RETURN count(*) AS disconnect_this_post0_creator0_disconnect_User + } + RETURN count(*) AS update_this_post0 + } + RETURN collect(DISTINCT this { .content }) AS data" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"updateComments_args_update_post0_update_node_creator0_disconnect0_where_User_this_post0_creator0_disconnect0param0\\": \\"user-id\\", + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"authorization__before_param2\\": \\"super-admin\\", + \\"authorization__before_param3\\": \\"admin\\", + \\"authorization__after_param2\\": \\"super-admin\\", + \\"authorization__after_param3\\": \\"admin\\", + \\"updateComments\\": { + \\"args\\": { + \\"update\\": { + \\"post\\": [ + { + \\"update\\": { + \\"node\\": { + \\"creator\\": [ + { + \\"disconnect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id_EQ\\": \\"user-id\\" + } + } + } + ] + } + ] + } + } + } + ] + } + } + }, + \\"resolvedCallbacks\\": {} + }" + `); + }); + + test("Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteUsers { + nodesDeleted + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"param2\\": \\"admin\\" + }" + `); + }); + + test("Nested Delete", async () => { + const query = /* GraphQL */ ` + mutation { + deleteUsers(delete: { posts: { where: {} } }) { + nodesDeleted + } + } + `; + + const token = createBearerToken("secret", { sub: "super_admin", roles: ["admin"] }); + const result = await translateQuery(neoSchema, query, { + token, + }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + MATCH (this:User) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH * + CALL { + WITH * + OPTIONAL MATCH (this)-[this0:HAS_POST]->(this1:Post) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH this0, collect(DISTINCT this1) AS var2 + CALL { + WITH var2 + UNWIND var2 AS var3 + DETACH DELETE var3 + } + } + WITH * + DETACH DELETE this" + `); + + expect(formatParams(result.params)).toMatchInlineSnapshot(` + "{ + \\"isAuthenticated\\": true, + \\"jwt\\": { + \\"roles\\": [ + \\"admin\\" + ], + \\"sub\\": \\"super_admin\\" + }, + \\"param2\\": \\"admin\\", + \\"param3\\": \\"super-admin\\" + }" + `); + }); +}); diff --git a/packages/graphql/tests/tck/deprecated/query-direction-aggregations.test.ts b/packages/graphql/tests/tck/deprecated/query-direction-aggregations.test.ts deleted file mode 100644 index accb8b3c69..0000000000 --- a/packages/graphql/tests/tck/deprecated/query-direction-aggregations.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("QueryDirection in relationships aggregations (deprecated _DEFAULT/_ONLY options)", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with directed and undirected relationships with a DEFAULT_UNDIRECTED", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Users { - users { - friendsAggregate { - count - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - RETURN count(this1) AS var2 - } - RETURN this { friendsAggregate: { count: var2 } } AS this" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query connection with a DIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Users { - users { - friendsAggregate { - count - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - RETURN count(this1) AS var2 - } - RETURN this { friendsAggregate: { count: var2 } } AS this" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with a UNDIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Users { - users { - friendsAggregate { - count - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - RETURN count(this1) AS var2 - } - RETURN this { friendsAggregate: { count: var2 } } AS this" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/deprecated/query-direction-connection.test.ts b/packages/graphql/tests/tck/deprecated/query-direction-connection.test.ts deleted file mode 100644 index 3de3db6fd4..0000000000 --- a/packages/graphql/tests/tck/deprecated/query-direction-connection.test.ts +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("QueryDirection in relationships connection (deprecated _DEFAULT/_ONLY options)", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with directed and undirected relationships with a DEFAULT_UNDIRECTED", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query FriendsAggregate { - users { - friendsConnection { - totalCount - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH collect({ node: this1, relationship: this0 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { __id: id(this1), __resolveType: \\"User\\" } }) AS var2 - } - RETURN { edges: var2, totalCount: totalCount } AS var3 - } - RETURN this { friendsConnection: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query connection with a DIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query FriendsAggregate { - users { - friendsConnection { - totalCount - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - WITH collect({ node: this1, relationship: this0 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { __id: id(this1), __resolveType: \\"User\\" } }) AS var2 - } - RETURN { edges: var2, totalCount: totalCount } AS var3 - } - RETURN this { friendsConnection: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with a UNDIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query FriendsAggregate { - users { - friendsConnection { - totalCount - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH collect({ node: this1, relationship: this0 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { __id: id(this1), __resolveType: \\"User\\" } }) AS var2 - } - RETURN { edges: var2, totalCount: totalCount } AS var3 - } - RETURN this { friendsConnection: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - -}); diff --git a/packages/graphql/tests/tck/deprecated/query-direction.test.ts b/packages/graphql/tests/tck/deprecated/query-direction.test.ts deleted file mode 100644 index ded623331c..0000000000 --- a/packages/graphql/tests/tck/deprecated/query-direction.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("QueryDirection in relationships (deprecated _DEFAULT/_ONLY options)", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with directed and undirected relationships with DEFAULT_UNDIRECTED", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - directedFriends: friends(directed: true) { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - CALL { - WITH this - MATCH (this)-[this3:FRIENDS_WITH]->(this4:User) - WITH this4 { .name } AS this4 - RETURN collect(this4) AS var5 - } - RETURN this { .name, friends: var2, directedFriends: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with directed and undirected relationships with a DEFAULT_DIRECTED", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - undirectedFriends: friends(directed: false) { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - CALL { - WITH this - MATCH (this)-[this3:FRIENDS_WITH]-(this4:User) - WITH this4 { .name } AS this4 - RETURN collect(this4) AS var5 - } - RETURN this { .name, friends: var2, undirectedFriends: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with a DIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with a UNDIRECTED_ONLY relationship", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/deprecated/update.test.ts b/packages/graphql/tests/tck/deprecated/update.test.ts deleted file mode 100644 index fd04432d7c..0000000000 --- a/packages/graphql/tests/tck/deprecated/update.test.ts +++ /dev/null @@ -1,995 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Cypher Update", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Actor @node { - name: String - movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - } - - type Movie @node { - id: ID - title: String - actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN) - } - - type ActedIn @relationshipProperties { - screenTime: Int - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Simple Update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies(where: { id_EQ: "1" }, update: { id: "2" }) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - SET this.id = $this_update_id - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"this_update_id\\": \\"2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Single Nested Update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: [{ where: { node: { name_EQ: "old name" } }, update: { node: { name: "new name" } } }] - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - MATCH (this)<-[this_acted_in0_relationship:ACTED_IN]-(this_actors0:Actor) - WHERE this_actors0.name = $updateMovies_args_update_actors0_where_this_actors0param0 - SET this_actors0.name = $this_update_actors0_name - RETURN count(*) AS update_this_actors0 - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_where_this_actors0param0\\": \\"old name\\", - \\"this_update_actors0_name\\": \\"new name\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"old name\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"new name\\" - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Double Nested Update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: [ - { - where: { node: { name_EQ: "old actor name" } } - update: { - node: { - name: "new actor name" - movies: [ - { - where: { node: { id_EQ: "old movie title" } } - update: { node: { title: "new movie title" } } - } - ] - } - } - } - ] - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - MATCH (this)<-[this_acted_in0_relationship:ACTED_IN]-(this_actors0:Actor) - WHERE this_actors0.name = $updateMovies_args_update_actors0_where_this_actors0param0 - SET this_actors0.name = $this_update_actors0_name - WITH this, this_actors0 - CALL { - WITH this, this_actors0 - MATCH (this_actors0)-[this_actors0_acted_in0_relationship:ACTED_IN]->(this_actors0_movies0:Movie) - WHERE this_actors0_movies0.id = $updateMovies_args_update_actors0_update_node_movies0_where_this_actors0_movies0param0 - SET this_actors0_movies0.title = $this_update_actors0_movies0_title - RETURN count(*) AS update_this_actors0_movies0 - } - RETURN count(*) AS update_this_actors0 - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_where_this_actors0param0\\": \\"old actor name\\", - \\"this_update_actors0_name\\": \\"new actor name\\", - \\"updateMovies_args_update_actors0_update_node_movies0_where_this_actors0_movies0param0\\": \\"old movie title\\", - \\"this_update_actors0_movies0_title\\": \\"new movie title\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"old actor name\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"new actor name\\", - \\"movies\\": [ - { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"old movie title\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"title\\": \\"new movie title\\" - } - } - } - ] - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Simple Update as Connect", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { actors: { connect: [{ where: { node: { name_EQ: "Daniel" } } }] } } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_actors0_connect0_node:Actor) - WHERE this_actors0_connect0_node.name = $this_actors0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_actors0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) - } - } - WITH this, this_actors0_connect0_node - RETURN count(*) AS connect_this_actors0_connect_Actor0 - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"this_actors0_connect0_node_param0\\": \\"Daniel\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update as multiple Connect", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: { - connect: [ - { where: { node: { name_EQ: "Daniel" } } } - { where: { node: { name_EQ: "Darrell" } } } - ] - } - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_actors0_connect0_node:Actor) - WHERE this_actors0_connect0_node.name = $this_actors0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_actors0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) - } - } - WITH this, this_actors0_connect0_node - RETURN count(*) AS connect_this_actors0_connect_Actor0 - } - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_actors0_connect1_node:Actor) - WHERE this_actors0_connect1_node.name = $this_actors0_connect1_node_param0 - CALL { - WITH * - WITH collect(this_actors0_connect1_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_actors0_connect1_node - MERGE (this)<-[this_actors0_connect1_relationship:ACTED_IN]-(this_actors0_connect1_node) - } - } - WITH this, this_actors0_connect1_node - RETURN count(*) AS connect_this_actors0_connect_Actor1 - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"this_actors0_connect0_node_param0\\": \\"Daniel\\", - \\"this_actors0_connect1_node_param0\\": \\"Darrell\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Simple Update as Disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { actors: { disconnect: [{ where: { node: { name_EQ: "Daniel" } } }] } } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_actors0_disconnect0_rel:ACTED_IN]-(this_actors0_disconnect0:Actor) - WHERE this_actors0_disconnect0.name = $updateMovies_args_update_actors0_disconnect0_where_Actor_this_actors0_disconnect0param0 - CALL { - WITH this_actors0_disconnect0, this_actors0_disconnect0_rel, this - WITH collect(this_actors0_disconnect0) as this_actors0_disconnect0, this_actors0_disconnect0_rel, this - UNWIND this_actors0_disconnect0 as x - DELETE this_actors0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_actors0_disconnect_Actor - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_disconnect0_where_Actor_this_actors0_disconnect0param0\\": \\"Daniel\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Daniel\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update as multiple Disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: { - disconnect: [ - { where: { node: { name_EQ: "Daniel" } } } - { where: { node: { name_EQ: "Darrell" } } } - ] - } - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_actors0_disconnect0_rel:ACTED_IN]-(this_actors0_disconnect0:Actor) - WHERE this_actors0_disconnect0.name = $updateMovies_args_update_actors0_disconnect0_where_Actor_this_actors0_disconnect0param0 - CALL { - WITH this_actors0_disconnect0, this_actors0_disconnect0_rel, this - WITH collect(this_actors0_disconnect0) as this_actors0_disconnect0, this_actors0_disconnect0_rel, this - UNWIND this_actors0_disconnect0 as x - DELETE this_actors0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_actors0_disconnect_Actor - } - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_actors0_disconnect1_rel:ACTED_IN]-(this_actors0_disconnect1:Actor) - WHERE this_actors0_disconnect1.name = $updateMovies_args_update_actors0_disconnect1_where_Actor_this_actors0_disconnect1param0 - CALL { - WITH this_actors0_disconnect1, this_actors0_disconnect1_rel, this - WITH collect(this_actors0_disconnect1) as this_actors0_disconnect1, this_actors0_disconnect1_rel, this - UNWIND this_actors0_disconnect1 as x - DELETE this_actors0_disconnect1_rel - } - RETURN count(*) AS disconnect_this_actors0_disconnect_Actor - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_disconnect0_where_Actor_this_actors0_disconnect0param0\\": \\"Daniel\\", - \\"updateMovies_args_update_actors0_disconnect1_where_Actor_this_actors0_disconnect1param0\\": \\"Darrell\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Daniel\\" - } - } - }, - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Darrell\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update an Actor while creating and connecting to a new Movie (via field level)", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - where: { name_EQ: "Dan" } - update: { movies: { create: [{ node: { id: "dan_movie_id", title: "The Story of Beer" } }] } } - ) { - actors { - name - movies { - id - title - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - WITH this - CREATE (this_movies0_create0_node:Movie) - SET this_movies0_create0_node.id = $this_movies0_create0_node_id - SET this_movies0_create0_node.title = $this_movies0_create0_node_title - MERGE (this)-[:ACTED_IN]->(this_movies0_create0_node) - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) - WITH update_this1 { .id, .title } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, movies: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Dan\\", - \\"this_movies0_create0_node_id\\": \\"dan_movie_id\\", - \\"this_movies0_create0_node_title\\": \\"The Story of Beer\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update an Actor while creating and connecting to a new Movie (via top level)", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - where: { name_EQ: "Dan" } - update: { movies: { create: [{ node: { id: "dan_movie_id", title: "The Story of Beer" } }] } } - ) { - actors { - name - movies { - id - title - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - WITH this - CREATE (this_movies0_create0_node:Movie) - SET this_movies0_create0_node.id = $this_movies0_create0_node_id - SET this_movies0_create0_node.title = $this_movies0_create0_node_title - MERGE (this)-[:ACTED_IN]->(this_movies0_create0_node) - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) - WITH update_this1 { .id, .title } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, movies: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Dan\\", - \\"this_movies0_create0_node_id\\": \\"dan_movie_id\\", - \\"this_movies0_create0_node_title\\": \\"The Story of Beer\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Update an Actor while creating and connecting to multiple new Movies (via top level)", async () => { - const query = /* GraphQL */ ` - mutation { - updateActors( - where: { name_EQ: "Dan" } - update: { - movies: { - create: [ - { node: { id: "dan_movie_id", title: "The Story of Beer" } } - { node: { id: "dan_movie2_id", title: "Forrest Gump" } } - ] - } - } - ) { - actors { - name - movies { - id - title - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - WHERE this.name = $param0 - WITH this - CREATE (this_movies0_create0_node:Movie) - SET this_movies0_create0_node.id = $this_movies0_create0_node_id - SET this_movies0_create0_node.title = $this_movies0_create0_node_title - MERGE (this)-[:ACTED_IN]->(this_movies0_create0_node) - CREATE (this_movies0_create1_node:Movie) - SET this_movies0_create1_node.id = $this_movies0_create1_node_id - SET this_movies0_create1_node.title = $this_movies0_create1_node_title - MERGE (this)-[:ACTED_IN]->(this_movies0_create1_node) - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) - WITH update_this1 { .id, .title } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, movies: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Dan\\", - \\"this_movies0_create0_node_id\\": \\"dan_movie_id\\", - \\"this_movies0_create0_node_title\\": \\"The Story of Beer\\", - \\"this_movies0_create1_node_id\\": \\"dan_movie2_id\\", - \\"this_movies0_create1_node_title\\": \\"Forrest Gump\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Delete related node as update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: { - delete: { where: { node: { name_EQ: "Actor to delete" }, edge: { screenTime_EQ: 60 } } } - } - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_actors0_delete0_relationship:ACTED_IN]-(this_actors0_delete0:Actor) - WHERE (this_actors0_delete0.name = $updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0 AND this_actors0_delete0_relationship.screenTime = $updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param1) - WITH this_actors0_delete0_relationship, collect(DISTINCT this_actors0_delete0) AS this_actors0_delete0_to_delete - CALL { - WITH this_actors0_delete0_to_delete - UNWIND this_actors0_delete0_to_delete AS x - DETACH DELETE x - } - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0\\": \\"Actor to delete\\", - \\"updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param1\\": { - \\"low\\": 60, - \\"high\\": 0 - }, - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" - }, - \\"edge\\": { - \\"screenTime_EQ\\": { - \\"low\\": 60, - \\"high\\": 0 - } - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Delete and update nested operations under same mutation", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: { - where: { node: { name_EQ: "Actor to update" } } - update: { node: { name: "Updated name" } } - delete: { where: { node: { name_EQ: "Actor to delete" } } } - } - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_actors0_delete0_relationship:ACTED_IN]-(this_actors0_delete0:Actor) - WHERE this_actors0_delete0.name = $updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0 - WITH this_actors0_delete0_relationship, collect(DISTINCT this_actors0_delete0) AS this_actors0_delete0_to_delete - CALL { - WITH this_actors0_delete0_to_delete - UNWIND this_actors0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH this - CALL { - WITH this - MATCH (this)<-[this_acted_in0_relationship:ACTED_IN]-(this_actors0:Actor) - WHERE this_actors0.name = $updateMovies_args_update_actors0_where_this_actors0param0 - SET this_actors0.name = $this_update_actors0_name - RETURN count(*) AS update_this_actors0 - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0\\": \\"Actor to delete\\", - \\"updateMovies_args_update_actors0_where_this_actors0param0\\": \\"Actor to update\\", - \\"this_update_actors0_name\\": \\"Updated name\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Actor to update\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"Updated name\\" - } - }, - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Nested delete under a nested update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { actors: { delete: { where: { node: { name_EQ: "Actor to delete" } } } } } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_actors0_delete0_relationship:ACTED_IN]-(this_actors0_delete0:Actor) - WHERE this_actors0_delete0.name = $updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0 - WITH this_actors0_delete0_relationship, collect(DISTINCT this_actors0_delete0) AS this_actors0_delete0_to_delete - CALL { - WITH this_actors0_delete0_to_delete - UNWIND this_actors0_delete0_to_delete AS x - DETACH DELETE x - } - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0\\": \\"Actor to delete\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("Double nested delete under a nested update", async () => { - const query = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "1" } - update: { - actors: { - delete: { - where: { node: { name_EQ: "Actor to delete" } } - delete: { movies: { where: { node: { id_EQ: "2" } } } } - } - } - } - ) { - movies { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_actors0_delete0_relationship:ACTED_IN]-(this_actors0_delete0:Actor) - WHERE this_actors0_delete0.name = $updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this_actors0_delete0)-[this_actors0_delete0_movies0_relationship:ACTED_IN]->(this_actors0_delete0_movies0:Movie) - WHERE this_actors0_delete0_movies0.id = $updateMovies_args_update_actors0_delete0_delete_movies0_where_this_actors0_delete0_movies0param0 - WITH this_actors0_delete0_movies0_relationship, collect(DISTINCT this_actors0_delete0_movies0) AS this_actors0_delete0_movies0_to_delete - CALL { - WITH this_actors0_delete0_movies0_to_delete - UNWIND this_actors0_delete0_movies0_to_delete AS x - DETACH DELETE x - } - } - WITH this_actors0_delete0_relationship, collect(DISTINCT this_actors0_delete0) AS this_actors0_delete0_to_delete - CALL { - WITH this_actors0_delete0_to_delete - UNWIND this_actors0_delete0_to_delete AS x - DETACH DELETE x - } - } - RETURN collect(DISTINCT this { .id }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"updateMovies_args_update_actors0_delete0_where_this_actors0_delete0param0\\": \\"Actor to delete\\", - \\"updateMovies_args_update_actors0_delete0_delete_movies0_where_this_actors0_delete0_movies0param0\\": \\"2\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"actors\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" - } - }, - \\"delete\\": { - \\"movies\\": [ - { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"2\\" - } - } - } - ] - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/directives/alias.test.ts b/packages/graphql/tests/tck/directives/alias.test.ts index 962bc4056f..2c57d9bb6c 100644 --- a/packages/graphql/tests/tck/directives/alias.test.ts +++ b/packages/graphql/tests/tck/directives/alias.test.ts @@ -65,10 +65,12 @@ describe("Cypher alias directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WITH DISTINCT this1 WITH this1 { .title, rating: this1.ratingPropInDb } AS this1 RETURN collect(this1) AS var2 } @@ -103,7 +105,8 @@ describe("Cypher alias directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -167,7 +170,8 @@ describe("Cypher alias directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Actor) @@ -193,6 +197,7 @@ describe("Cypher alias directive", () => { CALL { WITH create_this1 MATCH (create_this1)-[create_this6:ACTED_IN]->(create_this7:Movie) + WITH DISTINCT create_this7 WITH create_this7 { .title, rating: create_this7.ratingPropInDb } AS create_this7 RETURN collect(create_this7) AS create_var8 } diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/allow/allow.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/allow/allow.test.ts index ac05f7bb55..8b9fb5c573 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/allow/allow.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/allow/allow.test.ts @@ -31,14 +31,14 @@ describe("Cypher Auth Allow", () => { type Comment @node { id: ID content: String - creator: User! @relationship(type: "HAS_COMMENT", direction: IN) - post: Post! @relationship(type: "HAS_COMMENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_COMMENT", direction: IN) + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) } type Post @node { id: ID content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) comments: [Comment!]! @relationship(type: "HAS_COMMENT", direction: OUT) } @@ -54,7 +54,7 @@ describe("Cypher Auth Allow", () => { { operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] when: BEFORE - where: { node: { id_EQ: "$jwt.sub" } } + where: { node: { id: { eq: "$jwt.sub" } } } } ] ) @@ -63,7 +63,11 @@ describe("Cypher Auth Allow", () => { password: String! @authorization( validate: [ - { operations: [READ, UPDATE, DELETE], when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } } + { + operations: [READ, UPDATE, DELETE] + when: BEFORE + where: { node: { id: { eq: "$jwt.sub" } } } + } ] ) } @@ -74,7 +78,7 @@ describe("Cypher Auth Allow", () => { { operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] when: BEFORE - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) @@ -85,7 +89,7 @@ describe("Cypher Auth Allow", () => { { operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] when: BEFORE - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) @@ -116,7 +120,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .id } AS this" @@ -150,7 +155,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN this { .password } AS this" @@ -187,20 +193,23 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 + WITH DISTINCT this1 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this1 { .content } AS this1 - RETURN collect(this1) AS var4 + RETURN collect(this1) AS var3 } - RETURN this { .id, posts: var4 } AS this" + RETURN this { .id, posts: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -233,20 +242,23 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_POST]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this - MATCH (this)<-[this2:HAS_POST]-(this3:User) + MATCH (this)<-[this1:HAS_POST]-(this2:User) + WITH DISTINCT this2 WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - WITH this3 { .password } AS this3 - RETURN head(collect(this3)) AS var4 + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this2 { .password } AS this2 + RETURN collect(this2) AS var3 } - RETURN this { creator: var4 } AS this" + RETURN this { creator: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -265,10 +277,10 @@ describe("Cypher Auth Allow", () => { test("Read Two Relationships", async () => { const query = /* GraphQL */ ` { - users(where: { id_EQ: "1" }) { + users(where: { id: { eq: "1" } }) { id - posts(where: { id_EQ: "1" }) { - comments(where: { id_EQ: "1" }) { + posts(where: { id: { eq: "1" } }) { + comments(where: { id: { eq: "1" } }) { content } } @@ -282,30 +294,35 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 + WITH DISTINCT this1 WITH * - WHERE (this1.id = $param3 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this1.id = $param3 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this1 - MATCH (this1)-[this4:HAS_COMMENT]->(this5:Comment) - OPTIONAL MATCH (this5)<-[:HAS_COMMENT]-(this6:User) - WITH *, count(this6) AS var7 + MATCH (this1)-[this3:HAS_COMMENT]->(this4:Comment) + WITH DISTINCT this4 WITH * - WHERE (this5.id = $param4 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var7 <> 0 AND ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - WITH this5 { .content } AS this5 - RETURN collect(this5) AS var8 + WHERE (this4.id = $param4 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this4)<-[:HAS_COMMENT]-(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this4 { .content } AS this4 + RETURN collect(this4) AS var6 } - WITH this1 { comments: var8 } AS this1 - RETURN collect(this1) AS var9 + WITH this1 { comments: var6 } AS this1 + RETURN collect(this1) AS var7 } - RETURN this { .id, posts: var9 } AS this" + RETURN this { .id, posts: var7 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -327,7 +344,7 @@ describe("Cypher Auth Allow", () => { test("Update Node", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "old-id" }, update: { id_SET: "new-id" }) { + updateUsers(where: { id: { eq: "old-id" } }, update: { id_SET: "new-id" }) { users { id } @@ -341,7 +358,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) SET this.id = $this_update_id_SET @@ -369,7 +387,7 @@ describe("Cypher Auth Allow", () => { test("Update Node Property", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "id-01" }, update: { password_SET: "new-password" }) { + updateUsers(where: { id: { eq: "id-01" } }, update: { password_SET: "new-password" }) { users { id } @@ -383,7 +401,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this @@ -414,7 +433,7 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updatePosts( - where: { id_EQ: "post-id" } + where: { id: { eq: "post-id" } } update: { creator: { update: { node: { id_SET: "new-id" } } } } ) { posts { @@ -430,11 +449,13 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_POST]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this CALL { WITH this @@ -444,17 +465,10 @@ describe("Cypher Auth Allow", () => { RETURN count(*) AS update_this_creator0 } WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_POST]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } - OPTIONAL MATCH (this)<-[:HAS_POST]-(update_this0:User) - WITH *, count(update_this0) AS update_var1 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (update_var1 <> 0 AND ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(update_this0:User) + WHERE ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN collect(DISTINCT this { .id }) AS data" `); @@ -478,7 +492,7 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updatePosts( - where: { id_EQ: "post-id" } + where: { id: { eq: "post-id" } } update: { creator: { update: { node: { password_SET: "new-password" } } } } ) { posts { @@ -494,11 +508,13 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_POST]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this CALL { WITH this @@ -510,17 +526,10 @@ describe("Cypher Auth Allow", () => { RETURN count(*) AS update_this_creator0 } WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_POST]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } - OPTIONAL MATCH (this)<-[:HAS_POST]-(update_this0:User) - WITH *, count(update_this0) AS update_var1 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (update_var1 <> 0 AND ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(update_this0:User) + WHERE ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN collect(DISTINCT this { .id }) AS data" `); @@ -543,7 +552,7 @@ describe("Cypher Auth Allow", () => { test("Delete Node", async () => { const query = /* GraphQL */ ` mutation { - deleteUsers(where: { id_EQ: "user-id" }) { + deleteUsers(where: { id: { eq: "user-id" } }) { nodesDeleted } } @@ -555,7 +564,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) DETACH DELETE this" `); @@ -577,7 +587,10 @@ describe("Cypher Auth Allow", () => { test("Nested Delete Node", async () => { const query = /* GraphQL */ ` mutation { - deleteUsers(where: { id_EQ: "user-id" }, delete: { posts: { where: { node: { id_EQ: "post-id" } } } }) { + deleteUsers( + where: { id: { eq: "user-id" } } + delete: { posts: { where: { node: { id: { eq: "post-id" } } } } } + ) { nodesDeleted } } @@ -589,20 +602,22 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH * CALL { WITH * OPTIONAL MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WHERE (this1.id = $param3 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - WITH this0, collect(DISTINCT this1) AS var4 + WHERE (this1.id = $param3 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this0, collect(DISTINCT this1) AS var3 CALL { - WITH var4 - UNWIND var4 AS var5 - DETACH DELETE var5 + WITH var3 + UNWIND var3 AS var4 + DETACH DELETE var4 } } WITH * @@ -628,8 +643,8 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { id_EQ: "user-id" } - update: { posts: { disconnect: { where: { node: { id_EQ: "post-id" } } } } } + where: { id: { eq: "user-id" } } + update: { posts: { disconnect: { where: { node: { id: { eq: "post-id" } } } } } } ) { users { id @@ -644,17 +659,18 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) - OPTIONAL MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this @@ -688,7 +704,9 @@ describe("Cypher Auth Allow", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"post-id\\" + \\"id\\": { + \\"eq\\": \\"post-id\\" + } } } } @@ -707,9 +725,11 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updateComments( - where: { id_EQ: "comment-id" } + where: { id: { eq: "comment-id" } } update: { - post: { disconnect: { disconnect: { creator: { where: { node: { id_EQ: "user-id" } } } } } } + post: { + disconnect: { disconnect: { creator: { where: { node: { id: { eq: "user-id" } } } } } } + } } ) { comments { @@ -725,21 +745,24 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) - OPTIONAL MATCH (this)<-[:HAS_COMMENT]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Comment) WITH * - WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_COMMENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this CALL { WITH this OPTIONAL MATCH (this)<-[this_post0_disconnect0_rel:HAS_COMMENT]-(this_post0_disconnect0:Post) - OPTIONAL MATCH (this)<-[:HAS_COMMENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - OPTIONAL MATCH (this_post0_disconnect0)<-[:HAS_POST]-(authorization__before_this3:User) - WITH *, count(authorization__before_this3) AS authorization__before_var2 - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__before_var2 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this3.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_COMMENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_post0_disconnect0)<-[:HAS_POST]-(authorization__before_this1:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_post0_disconnect0, this_post0_disconnect0_rel, this WITH collect(this_post0_disconnect0) as this_post0_disconnect0, this_post0_disconnect0_rel, this @@ -749,10 +772,10 @@ describe("Cypher Auth Allow", () => { CALL { WITH this, this_post0_disconnect0 OPTIONAL MATCH (this_post0_disconnect0)<-[this_post0_disconnect0_creator0_rel:HAS_POST]-(this_post0_disconnect0_creator0:User) - OPTIONAL MATCH (this_post0_disconnect0)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_post0_disconnect0_creator0.id = $updateComments_args_update_post_disconnect_disconnect_creator_where_User_this_post0_disconnect0_creator0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_post0_disconnect0_creator0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_post0_disconnect0_creator0.id = $updateComments_args_update_post0_disconnect0_disconnect_creator0_where_User_this_post0_disconnect0_creator0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_post0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_post0_disconnect0_creator0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_post0_disconnect0_creator0, this_post0_disconnect0_creator0_rel, this_post0_disconnect0 WITH collect(this_post0_disconnect0_creator0) as this_post0_disconnect0_creator0, this_post0_disconnect0_creator0_rel, this_post0_disconnect0 @@ -764,24 +787,10 @@ describe("Cypher Auth Allow", () => { RETURN count(*) AS disconnect_this_post0_disconnect_Post } WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_COMMENT]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } - CALL { - WITH this - MATCH (this)<-[this_post_Post_unique:HAS_COMMENT]-(:Post) - WITH count(this_post_Post_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.post required exactly once', [0]) - RETURN c AS this_post_Post_unique_ignored - } - OPTIONAL MATCH (this)<-[:HAS_COMMENT]-(update_this0:User) - WITH *, count(update_this0) AS update_var1 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (update_var1 <> 0 AND ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_COMMENT]-(update_this0:User) + WHERE ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN collect(DISTINCT this { .id }) AS data" `); @@ -795,23 +804,31 @@ describe("Cypher Auth Allow", () => { \\"sub\\": \\"user-id\\" }, \\"param0\\": \\"comment-id\\", - \\"updateComments_args_update_post_disconnect_disconnect_creator_where_User_this_post0_disconnect0_creator0param0\\": \\"user-id\\", + \\"updateComments_args_update_post0_disconnect0_disconnect_creator0_where_User_this_post0_disconnect0_creator0param0\\": \\"user-id\\", \\"updateComments\\": { \\"args\\": { \\"update\\": { - \\"post\\": { - \\"disconnect\\": { - \\"disconnect\\": { - \\"creator\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"user-id\\" - } + \\"post\\": [ + { + \\"disconnect\\": [ + { + \\"disconnect\\": { + \\"creator\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"user-id\\" + } + } + } + } + ] } } - } + ] } - } + ] } } }, @@ -824,8 +841,8 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { id_EQ: "user-id" } - update: { posts: { connect: { where: { node: { id_EQ: "post-id" } } } } } + where: { id: { eq: "user-id" } } + update: { posts: { connect: { where: { node: { id: { eq: "post-id" } } } } } } ) { users { id @@ -840,17 +857,18 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH * CALL { WITH this OPTIONAL MATCH (this_posts0_connect0_node:Post) - OPTIONAL MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH * WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -858,7 +876,7 @@ describe("Cypher Auth Allow", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/allow/interface-relationships/implementation-allow.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/allow/interface-relationships/implementation-allow.test.ts index e6487e7d29..d48dd64be2 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/allow/interface-relationships/implementation-allow.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/allow/interface-relationships/implementation-allow.test.ts @@ -31,14 +31,14 @@ describe("@auth allow on specific interface implementation", () => { interface Content { id: ID content: String - creator: User! @declareRelationship + creator: [User!]! @declareRelationship } type Comment implements Content @node { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) - post: Post! @relationship(type: "HAS_COMMENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) } type Post implements Content @@ -48,13 +48,13 @@ describe("@auth allow on specific interface implementation", () => { { when: BEFORE operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) comments: [Comment!]! @relationship(type: "HAS_COMMENT", direction: OUT) } @@ -94,7 +94,8 @@ describe("@auth allow on specific interface implementation", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this CALL { @@ -105,10 +106,10 @@ describe("@auth allow on specific interface implementation", () => { UNION WITH * MATCH (this)-[this3:HAS_CONTENT]->(this4:Post) - OPTIONAL MATCH (this4)<-[:HAS_CONTENT]-(this5:User) - WITH *, count(this5) AS var6 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var6 <> 0 AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this4)<-[:HAS_CONTENT]-(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this4 { .id, .content, __resolveType: \\"Post\\", __id: id(this4) } AS this4 RETURN this4 AS var2 } @@ -134,11 +135,11 @@ describe("@auth allow on specific interface implementation", () => { test("Read Two Relationships", async () => { const query = /* GraphQL */ ` { - users(where: { id_EQ: "1" }) { + users(where: { id: { eq: "1" } }) { id - content(where: { id_EQ: "1" }) { + content(where: { id: { eq: "1" } }) { ... on Post { - comments(where: { id_EQ: "1" }) { + comments(where: { id: { eq: "1" } }) { content } } @@ -153,7 +154,8 @@ describe("@auth allow on specific interface implementation", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 CALL { WITH this @@ -166,18 +168,19 @@ describe("@auth allow on specific interface implementation", () => { UNION WITH * MATCH (this)-[this3:HAS_CONTENT]->(this4:Post) - OPTIONAL MATCH (this4)<-[:HAS_CONTENT]-(this5:User) - WITH *, count(this5) AS var6 - WITH * - WHERE (this4.id = $param2 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var6 <> 0 AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this4.id = $param2 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this4)<-[:HAS_CONTENT]-(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this4 - MATCH (this4)-[this7:HAS_COMMENT]->(this8:Comment) - WHERE this8.id = $param5 - WITH this8 { .content } AS this8 - RETURN collect(this8) AS var9 + MATCH (this4)-[this6:HAS_COMMENT]->(this7:Comment) + WHERE this7.id = $param5 + WITH DISTINCT this7 + WITH this7 { .content } AS this7 + RETURN collect(this7) AS var8 } - WITH this4 { comments: var9, __resolveType: \\"Post\\", __id: id(this4) } AS this4 + WITH this4 { comments: var8, __resolveType: \\"Post\\", __id: id(this4) } AS this4 RETURN this4 AS var2 } WITH var2 @@ -207,7 +210,7 @@ describe("@auth allow on specific interface implementation", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { id_EQ: "user-id" } + where: { id: { eq: "user-id" } } update: { content: { update: { node: { id_SET: "new-id" } } } } ) { users { @@ -226,7 +229,8 @@ describe("@auth allow on specific interface implementation", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 WITH this CALL { @@ -236,21 +240,6 @@ describe("@auth allow on specific interface implementation", () => { WITH this MATCH (this)-[this_has_content0_relationship:HAS_CONTENT]->(this_content0:Comment) SET this_content0.id = $this_update_content0_id_SET - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_post_Post_unique:HAS_COMMENT]-(:Post) - WITH count(this_content0_post_Post_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.post required exactly once', [0]) - RETURN c AS this_content0_post_Post_unique_ignored - } RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Comment @@ -261,18 +250,11 @@ describe("@auth allow on specific interface implementation", () => { CALL { WITH this MATCH (this)-[this_has_content0_relationship:HAS_CONTENT]->(this_content0:Post) - OPTIONAL MATCH (this_content0)<-[:HAS_CONTENT]-(authorization_updatebefore_this1:User) - WITH *, count(authorization_updatebefore_this1) AS authorization_updatebefore_var0 - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_updatebefore_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_updatebefore_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_content0)<-[:HAS_CONTENT]-(authorization_updatebefore_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_updatebefore_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) SET this_content0.id = $this_update_content0_id_SET - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Post @@ -288,10 +270,10 @@ describe("@auth allow on specific interface implementation", () => { UNION WITH * MATCH (this)-[update_this3:HAS_CONTENT]->(update_this4:Post) - OPTIONAL MATCH (update_this4)<-[:HAS_CONTENT]-(update_this5:User) - WITH *, count(update_this5) AS update_var6 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (update_var6 <> 0 AND ($jwt.sub IS NOT NULL AND update_this5.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (update_this4)<-[:HAS_CONTENT]-(update_this5:User) + WHERE ($jwt.sub IS NOT NULL AND update_this5.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH update_this4 { .id, __resolveType: \\"Post\\", __id: id(update_this4) } AS update_this4 RETURN update_this4 AS update_var2 } @@ -321,8 +303,8 @@ describe("@auth allow on specific interface implementation", () => { const query = /* GraphQL */ ` mutation { deleteUsers( - where: { id_EQ: "user-id" } - delete: { content: { where: { node: { id_EQ: "post-id" } } } } + where: { id: { eq: "user-id" } } + delete: { content: { where: { node: { id: { eq: "post-id" } } } } } ) { nodesDeleted } @@ -335,7 +317,8 @@ describe("@auth allow on specific interface implementation", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 WITH * CALL { @@ -352,14 +335,15 @@ describe("@auth allow on specific interface implementation", () => { CALL { WITH * OPTIONAL MATCH (this)-[this4:HAS_CONTENT]->(this5:Post) - OPTIONAL MATCH (this5)<-[:HAS_CONTENT]-(this6:User) - WITH *, count(this6) AS var7 - WHERE (this5.id = $param2 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var7 <> 0 AND ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - WITH this4, collect(DISTINCT this5) AS var8 + WHERE (this5.id = $param2 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this5)<-[:HAS_CONTENT]-(this6:User) + WHERE ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this4, collect(DISTINCT this5) AS var7 CALL { - WITH var8 - UNWIND var8 AS var9 - DETACH DELETE var9 + WITH var7 + UNWIND var7 AS var8 + DETACH DELETE var8 } } WITH * diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/interface-relationships/implementation-is-authenticated.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/interface-relationships/implementation-is-authenticated.test.ts index 025df07d9a..4aee939bbb 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/interface-relationships/implementation-is-authenticated.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/interface-relationships/implementation-is-authenticated.test.ts @@ -78,7 +78,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Post) @@ -119,7 +120,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Comment) @@ -146,7 +148,7 @@ describe("Cypher Auth isAuthenticated", () => { test("Update Node with bind", async () => { const query = /* GraphQL */ ` mutation { - updatePosts(where: { id_EQ: "1" }, update: { id_SET: "id-1" }) { + updatePosts(where: { id: { eq: "1" } }, update: { id_SET: "id-1" }) { posts { id } @@ -160,7 +162,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -178,7 +181,7 @@ describe("Cypher Auth isAuthenticated", () => { test("Update Node without bind", async () => { const query = /* GraphQL */ ` mutation { - updateComments(where: { id_EQ: "1" }, update: { id_SET: "id-1" }) { + updateComments(where: { id: { eq: "1" } }, update: { id_SET: "id-1" }) { comments { id } @@ -192,7 +195,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) + "CYPHER 5 + MATCH (this:Comment) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -222,7 +226,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) DETACH DELETE this" `); @@ -244,7 +249,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) + "CYPHER 5 + MATCH (this:Comment) DETACH DELETE this" `); @@ -266,7 +272,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * CALL { WITH * diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/is-authenticated.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/is-authenticated.test.ts index 75b6470a59..01f5d5e243 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/is-authenticated.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/is-authenticated/is-authenticated.test.ts @@ -81,7 +81,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .id, .name } AS this" `); @@ -105,7 +106,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .id, .name, .password } AS this" `); @@ -129,7 +131,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this CALL { @@ -164,7 +167,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -203,7 +207,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -230,7 +235,7 @@ describe("Cypher Auth isAuthenticated", () => { test("Update Node", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "1" }, update: { id_SET: "id-1" }) { + updateUsers(where: { id: { eq: "1" } }, update: { id_SET: "id-1" }) { users { id } @@ -244,7 +249,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -262,7 +268,7 @@ describe("Cypher Auth isAuthenticated", () => { test("Update Node & Field", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "1" }, update: { password_SET: "password" }) { + updateUsers(where: { id: { eq: "1" } }, update: { password_SET: "password" }) { users { id } @@ -276,7 +282,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 SET this.password = $this_update_password_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -306,7 +313,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) DETACH DELETE this" `); @@ -328,7 +336,8 @@ describe("Cypher Auth isAuthenticated", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * CALL { WITH * diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/roles-where.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/roles-where.test.ts index 56936e0c21..445ae45df9 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/roles-where.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/roles-where.test.ts @@ -44,34 +44,41 @@ describe("Cypher Auth Where with Roles", () => { type Post @node { id: ID content: String - creator: User @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } extend type User @authorization( validate: [ - { where: { node: { id_EQ: "$jwt.sub" }, jwt: { roles_INCLUDES: "user" } } } - { where: { jwt: { roles_INCLUDES: "admin" } } } + { where: { node: { id: { eq: "$jwt.sub" } }, jwt: { roles: { includes: "user" } } } } + { where: { jwt: { roles: { includes: "admin" } } } } ] ) extend type User { password: String! - @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) } extend type Post { secretKey: String! @authorization( - filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }] + filter: [ + { operations: [READ], where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } + ] ) } extend type Post @authorization( validate: [ - { where: { node: { creator: { id_EQ: "$jwt.sub" } }, jwt: { roles_INCLUDES: "user" } } } - { where: { jwt: { roles_INCLUDES: "admin" } } } + { + where: { + node: { creator: { some: { id: { eq: "$jwt.sub" } } } } + jwt: { roles: { includes: "user" } } + } + } + { where: { jwt: { roles: { includes: "admin" } } } } ] ) `; @@ -97,7 +104,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .id } AS this" @@ -121,7 +129,7 @@ describe("Cypher Auth Where with Roles", () => { test("Read Node + User Defined Where", async () => { const query = /* GraphQL */ ` { - users(where: { name_EQ: "bob" }) { + users(where: { name: { eq: "bob" } }) { id } } @@ -133,7 +141,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.name = $param0 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN this { .id } AS this" @@ -173,14 +182,19 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) + WITH DISTINCT this1 WITH * - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this1 { .content } AS this1 RETURN collect(this1) AS var3 } @@ -226,13 +240,17 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -268,7 +286,7 @@ describe("Cypher Auth Where with Roles", () => { { users { id - postsConnection(where: { node: { id_EQ: "some-id" } }) { + postsConnection(where: { node: { id: { eq: "some-id" } } }) { edges { node { content @@ -285,13 +303,17 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE (this1.id = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this1.id = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -328,7 +350,7 @@ describe("Cypher Auth Where with Roles", () => { { users { id - posts(where: { content_EQ: "cool" }) { + posts(where: { content: { eq: "cool" } }) { content } } @@ -341,14 +363,19 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) + WITH DISTINCT this1 WITH * - WHERE (this1.content = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this1.content = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this1 { .content } AS this1 RETURN collect(this1) AS var3 } @@ -393,7 +420,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { @@ -401,7 +429,10 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH * MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this1 { .id, __resolveType: \\"Post\\", __id: id(this1) } AS this1 RETURN this1 AS var3 } @@ -452,7 +483,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { @@ -460,7 +492,10 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH { node: { __resolveType: \\"Post\\", __id: id(this1), id: this1.id } } AS edge RETURN edge } @@ -493,7 +528,7 @@ describe("Cypher Auth Where with Roles", () => { { users { id - contentConnection(where: { Post: { node: { id_EQ: "some-id" } } }) { + contentConnection(where: { Post: { node: { id: { eq: "some-id" } } } }) { edges { node { ... on Post { @@ -512,7 +547,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { @@ -520,7 +556,10 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE (this1.id = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (this1.id = $param4 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH { node: { __resolveType: \\"Post\\", __id: id(this1), id: this1.id } } AS edge RETURN edge } @@ -566,7 +605,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) SET this.name = $this_update_name_SET @@ -601,7 +641,7 @@ describe("Cypher Auth Where with Roles", () => { test("Update Node + User Defined Where", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { name_EQ: "bob" }, update: { name_SET: "Bob" }) { + updateUsers(where: { name: { eq: "bob" } }, update: { name_SET: "Bob" }) { users { id } @@ -615,7 +655,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.name = $param0 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) SET this.name = $this_update_name_SET @@ -668,25 +709,24 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this CALL { WITH this MATCH (this)-[this_has_post0_relationship:HAS_POST]->(this_posts0:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization_updatebefore_this0 IN [(this_posts0)<-[:HAS_POST]-(authorization_updatebefore_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization_updatebefore_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization_updatebefore_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_updatebefore_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0)<-[:HAS_POST]-(authorization_updatebefore_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_updatebefore_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization_updatebefore_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_updatebefore_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) SET this_posts0.id = $this_update_posts0_id_SET WITH this, this_posts0 - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__after_this0 IN [(this_posts0)<-[:HAS_POST]-(authorization__after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH this, this_posts0 - CALL { - WITH this_posts0 - MATCH (this_posts0)<-[this_posts0_creator_User_unique:HAS_POST]-(:User) - WITH count(this_posts0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator must be less than or equal to one', [0]) - RETURN c AS this_posts0_creator_User_unique_ignored - } + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN count(*) AS update_this_posts0 } WITH this @@ -696,8 +736,12 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH this MATCH (this)-[update_this0:HAS_POST]->(update_this1:Post) + WITH DISTINCT update_this1 WITH * - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(update_this2 IN [(update_this1)<-[:HAS_POST]-(update_this2:User) WHERE ($jwt.sub IS NOT NULL AND update_this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $update_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (update_this1)<-[:HAS_POST]-(update_this2:User) + WHERE ($jwt.sub IS NOT NULL AND update_this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $update_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $update_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH update_this1 { .id } AS update_this1 RETURN collect(update_this1) AS update_var3 } @@ -744,7 +788,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) DETACH DELETE this" `); @@ -779,13 +824,17 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * CALL { WITH * OPTIONAL MATCH (this)-[this0:HAS_POST]->(this1:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(this2 IN [(this1)<-[:HAS_POST]-(this2:User) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this0, collect(DISTINCT this1) AS var3 CALL { WITH var3 @@ -835,7 +884,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -844,7 +894,10 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH this0 OPTIONAL MATCH (this0_posts_connect0_node:Post) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization_0_before_this0 IN [(this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH * WITH collect(this0_posts_connect0_node) as connectedNodes, collect(this0) as parentNodes @@ -852,12 +905,15 @@ describe("Cypher Auth Where with Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_posts_connect0_node - MERGE (this0)-[:HAS_POST]->(this0_posts_connect0_node) + CREATE (this0)-[:HAS_POST]->(this0_posts_connect0_node) } } WITH this0, this0_posts_connect0_node WITH this0, this0_posts_connect0_node - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization_0_after_this0 IN [(this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization_0_after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this0_posts_connect_Post0 } WITH * @@ -903,7 +959,7 @@ describe("Cypher Auth Where with Roles", () => { id: "123" name: "Bob" password: "password" - posts: { connect: { where: { node: { id_EQ: "post-id" } } } } + posts: { connect: { where: { node: { id: { eq: "post-id" } } } } } } ] ) { @@ -920,7 +976,8 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -929,7 +986,10 @@ describe("Cypher Auth Where with Roles", () => { CALL { WITH this0 OPTIONAL MATCH (this0_posts_connect0_node:Post) - WHERE this0_posts_connect0_node.id = $this0_posts_connect0_node_param0 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization_0_before_this0 IN [(this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE this0_posts_connect0_node.id = $this0_posts_connect0_node_param0 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH * WITH collect(this0_posts_connect0_node) as connectedNodes, collect(this0) as parentNodes @@ -937,12 +997,15 @@ describe("Cypher Auth Where with Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_posts_connect0_node - MERGE (this0)-[:HAS_POST]->(this0_posts_connect0_node) + CREATE (this0)-[:HAS_POST]->(this0_posts_connect0_node) } } WITH this0, this0_posts_connect0_node WITH this0, this0_posts_connect0_node - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization_0_after_this0 IN [(this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization_0_after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this0_posts_connect_Post0 } WITH * @@ -997,14 +1060,18 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * CALL { WITH this OPTIONAL MATCH (this_posts0_connect0_node:Post) - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__before_this0 IN [(this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH * WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -1012,12 +1079,15 @@ describe("Cypher Auth Where with Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node WITH this, this_posts0_connect0_node - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__after_this0 IN [(this_posts0_connect0_node)<-[:HAS_POST]-(authorization__after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this_posts0_connect_Post0 } WITH this @@ -1056,7 +1126,7 @@ describe("Cypher Auth Where with Roles", () => { test("Connect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { posts: { connect: { where: { node: { id_EQ: "new-id" } } } } }) { + updateUsers(update: { posts: { connect: { where: { node: { id: { eq: "new-id" } } } } } }) { users { id } @@ -1070,14 +1140,18 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * CALL { WITH this OPTIONAL MATCH (this_posts0_connect0_node:Post) - WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__before_this0 IN [(this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH * WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -1085,12 +1159,15 @@ describe("Cypher Auth Where with Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node WITH this, this_posts0_connect0_node - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__after_this0 IN [(this_posts0_connect0_node)<-[:HAS_POST]-(authorization__after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this_posts0_connect_Post0 } WITH this @@ -1144,14 +1221,18 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__before_this0 IN [(this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this @@ -1159,7 +1240,10 @@ describe("Cypher Auth Where with Roles", () => { DELETE this_posts0_disconnect0_rel } WITH this, this_posts0_disconnect0 - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__after_this0 IN [(this_posts0_disconnect0)<-[:HAS_POST]-(authorization__after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS disconnect_this_posts0_disconnect_Post } WITH this @@ -1198,7 +1282,7 @@ describe("Cypher Auth Where with Roles", () => { test("Disconnect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { posts: [{ disconnect: { where: { node: { id_EQ: "new-id" } } } }] }) { + updateUsers(update: { posts: [{ disconnect: { where: { node: { id: { eq: "new-id" } } } } }] }) { users { id } @@ -1212,14 +1296,18 @@ describe("Cypher Auth Where with Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) - WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__before_this0 IN [(this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__before_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this @@ -1227,7 +1315,10 @@ describe("Cypher Auth Where with Roles", () => { DELETE this_posts0_disconnect0_rel } WITH this, this_posts0_disconnect0 - WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND single(authorization__after_this0 IN [(this_posts0_disconnect0)<-[:HAS_POST]-(authorization__after_this0:User) WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) | 1] WHERE true) AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE (apoc.util.validatePredicate(NOT (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub) AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + } AND ($jwt.roles IS NOT NULL AND $authorization__after_param4 IN $jwt.roles)) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param5 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS disconnect_this_posts0_disconnect_Post } WITH this @@ -1268,7 +1359,9 @@ describe("Cypher Auth Where with Roles", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"new-id\\" + \\"id\\": { + \\"eq\\": \\"new-id\\" + } } } } diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/roles/roles.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/roles/roles.test.ts index 99c7fe30f6..ffb0e0db47 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/roles/roles.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/roles/roles.test.ts @@ -35,20 +35,20 @@ describe("Cypher Auth Roles", () => { type History @node { url: String @authorization( - validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "super-admin" } } }] + validate: [{ operations: [READ], where: { jwt: { roles: { includes: "super-admin" } } } }] ) } type Comment @node { id: String content: String - post: Post! @relationship(type: "HAS_COMMENT", direction: IN) + post: [Post!]! @relationship(type: "HAS_COMMENT", direction: IN) } type Post @node { id: String content: String - creator: User! @relationship(type: "HAS_POST", direction: OUT) + creator: [User!]! @relationship(type: "HAS_POST", direction: OUT) comments: [Comment!]! @relationship(type: "HAS_COMMENT", direction: OUT) } @@ -59,14 +59,14 @@ describe("Cypher Auth Roles", () => { posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type User @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) + extend type User @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }]) extend type Post @authorization( validate: [ { operations: [CREATE_RELATIONSHIP, DELETE_RELATIONSHIP, DELETE] - where: { jwt: { roles_INCLUDES: "super-admin" } } + where: { jwt: { roles: { includes: "super-admin" } } } } ] ) @@ -75,7 +75,10 @@ describe("Cypher Auth Roles", () => { password: String @authorization( validate: [ - { operations: [READ, CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "super-admin" } } } + { + operations: [READ, CREATE, UPDATE] + where: { jwt: { roles: { includes: "super-admin" } } } + } ] ) } @@ -84,7 +87,7 @@ describe("Cypher Auth Roles", () => { history: [History] @cypher(statement: "MATCH (this)-[:HAS_HISTORY]->(h:History) RETURN h", columnName: "h") @authorization( - validate: [{ operations: [READ], where: { jwt: { roles_INCLUDES: "super-admin" } } }] + validate: [{ operations: [READ], where: { jwt: { roles: { includes: "super-admin" } } } }] ) } `; @@ -111,7 +114,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .id, .name } AS this" @@ -148,7 +152,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN this { .id, .name, .password } AS this" @@ -186,7 +191,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { @@ -238,7 +244,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -288,7 +295,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -326,7 +334,7 @@ describe("Cypher Auth Roles", () => { test("Update Node", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "1" }, update: { id_SET: "id-1" }) { + updateUsers(where: { id: { eq: "1" } }, update: { id_SET: "id-1" }) { users { id } @@ -340,7 +348,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) SET this.id = $this_update_id_SET @@ -373,7 +382,7 @@ describe("Cypher Auth Roles", () => { test("Update Node & Field", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "1" }, update: { password_SET: "password" }) { + updateUsers(where: { id: { eq: "1" } }, update: { password_SET: "password" }) { users { id } @@ -387,7 +396,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this @@ -437,7 +447,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * @@ -452,7 +463,7 @@ describe("Cypher Auth Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node @@ -492,7 +503,9 @@ describe("Cypher Auth Roles", () => { mutation { updateComments( update: { - post: { update: { node: { creator: { connect: { where: { node: { id_EQ: "user-id" } } } } } } } + post: { + update: { node: { creator: { connect: { where: { node: { id: { eq: "user-id" } } } } } } } + } } ) { comments { @@ -508,7 +521,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) + "CYPHER 5 + MATCH (this:Comment) WITH this CALL { WITH this @@ -525,7 +539,7 @@ describe("Cypher Auth Roles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this_post0 UNWIND connectedNodes as this_post0_creator0_connect0_node - MERGE (this_post0)-[:HAS_POST]->(this_post0_creator0_connect0_node) + CREATE (this_post0)-[:HAS_POST]->(this_post0_creator0_connect0_node) } } WITH this, this_post0, this_post0_creator0_connect0_node @@ -533,24 +547,8 @@ describe("Cypher Auth Roles", () => { WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this_post0_creator0_connect_User0 } - WITH this, this_post0 - CALL { - WITH this_post0 - MATCH (this_post0)-[this_post0_creator_User_unique:HAS_POST]->(:User) - WITH count(this_post0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_post0_creator_User_unique_ignored - } RETURN count(*) AS update_this_post0 } - WITH * - CALL { - WITH this - MATCH (this)<-[this_post_Post_unique:HAS_COMMENT]-(:Post) - WITH count(this_post_Post_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.post required exactly once', [0]) - RETURN c AS this_post_Post_unique_ignored - } RETURN collect(DISTINCT this { .content }) AS data" `); @@ -590,7 +588,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this @@ -641,7 +640,9 @@ describe("Cypher Auth Roles", () => { updateComments( update: { post: { - update: { node: { creator: { disconnect: { where: { node: { id_EQ: "user-id" } } } } } } + update: { + node: { creator: { disconnect: { where: { node: { id: { eq: "user-id" } } } } } } + } } } ) { @@ -658,7 +659,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) + "CYPHER 5 + MATCH (this:Comment) WITH this CALL { WITH this @@ -667,7 +669,7 @@ describe("Cypher Auth Roles", () => { CALL { WITH this, this_post0 OPTIONAL MATCH (this_post0)-[this_post0_creator0_disconnect0_rel:HAS_POST]->(this_post0_creator0_disconnect0:User) - WHERE this_post0_creator0_disconnect0.id = $updateComments_args_update_post_update_node_creator_disconnect_where_User_this_post0_creator0_disconnect0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this_post0_creator0_disconnect0.id = $updateComments_args_update_post0_update_node_creator0_disconnect0_where_User_this_post0_creator0_disconnect0param0 AND (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__before_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH this_post0_creator0_disconnect0, this_post0_creator0_disconnect0_rel, this_post0 WITH collect(this_post0_creator0_disconnect0) as this_post0_creator0_disconnect0, this_post0_creator0_disconnect0_rel, this_post0 @@ -678,30 +680,14 @@ describe("Cypher Auth Roles", () => { WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization__after_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS disconnect_this_post0_creator0_disconnect_User } - WITH this, this_post0 - CALL { - WITH this_post0 - MATCH (this_post0)-[this_post0_creator_User_unique:HAS_POST]->(:User) - WITH count(this_post0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_post0_creator_User_unique_ignored - } RETURN count(*) AS update_this_post0 } - WITH * - CALL { - WITH this - MATCH (this)<-[this_post_Post_unique:HAS_COMMENT]-(:Post) - WITH count(this_post_Post_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.post required exactly once', [0]) - RETURN c AS this_post_Post_unique_ignored - } RETURN collect(DISTINCT this { .content }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"updateComments_args_update_post_update_node_creator_disconnect_where_User_this_post0_creator0_disconnect0param0\\": \\"user-id\\", + \\"updateComments_args_update_post0_update_node_creator0_disconnect0_where_User_this_post0_creator0_disconnect0param0\\": \\"user-id\\", \\"isAuthenticated\\": true, \\"jwt\\": { \\"roles\\": [ @@ -716,21 +702,29 @@ describe("Cypher Auth Roles", () => { \\"updateComments\\": { \\"args\\": { \\"update\\": { - \\"post\\": { - \\"update\\": { - \\"node\\": { - \\"creator\\": { - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"user-id\\" - } + \\"post\\": [ + { + \\"update\\": { + \\"node\\": { + \\"creator\\": [ + { + \\"disconnect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"user-id\\" + } + } + } + } + ] } - } + ] } } } - } + ] } } }, @@ -754,7 +748,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) DETACH DELETE this" `); @@ -788,7 +783,8 @@ describe("Cypher Auth Roles", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * CALL { diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/validate/interface-relationships/implementation-bind.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/validate/interface-relationships/implementation-bind.test.ts index d09937d771..3d3cca2943 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/validate/interface-relationships/implementation-bind.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/validate/interface-relationships/implementation-bind.test.ts @@ -30,12 +30,12 @@ describe("Cypher Auth Allow", () => { typeDefs = /* GraphQL */ ` interface Content { id: ID - creator: User! @declareRelationship + creator: [User!]! @declareRelationship } type Comment implements Content @node { id: ID - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) } type Post implements Content @@ -45,12 +45,12 @@ describe("Cypher Auth Allow", () => { { when: AFTER operations: [CREATE, UPDATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) { id: ID - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) } type User @node { @@ -65,7 +65,7 @@ describe("Cypher Auth Allow", () => { { when: AFTER operations: [CREATE, UPDATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { id_EQ: "$jwt.sub" } } + where: { node: { id: { eq: "$jwt.sub" } } } } ] ) @@ -117,7 +117,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -130,18 +131,10 @@ describe("Cypher Auth Allow", () => { MERGE (this0_contentPost0_node)<-[:HAS_CONTENT]-(this0_contentPost0_node_creator0_node) MERGE (this0)-[:HAS_CONTENT]->(this0_contentPost0_node) WITH * - CALL { - WITH this0_contentPost0_node - MATCH (this0_contentPost0_node)<-[this0_contentPost0_node_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this0_contentPost0_node_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this0_contentPost0_node_creator_User_unique_ignored - } - WITH * - OPTIONAL MATCH (this0_contentPost0_node)<-[:HAS_CONTENT]-(authorization_0_2_0_1_after_this1:User) - WITH *, count(authorization_0_2_0_1_after_this1) AS authorization_0_2_0_1_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0_contentPost0_node_creator0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_2_0_1_after_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_0_2_0_1_after_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0_contentPost0_node_creator0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_contentPost0_node)<-[:HAS_CONTENT]-(authorization_0_2_0_1_after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_2_0_1_after_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this0 } CALL { @@ -205,7 +198,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -218,14 +212,6 @@ describe("Cypher Auth Allow", () => { MERGE (this0_contentComment0_node)<-[:HAS_CONTENT]-(this0_contentComment0_node_creator0_node) MERGE (this0)-[:HAS_CONTENT]->(this0_contentComment0_node) WITH * - CALL { - WITH this0_contentComment0_node - MATCH (this0_contentComment0_node)<-[this0_contentComment0_node_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this0_contentComment0_node_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.creator required exactly once', [0]) - RETURN c AS this0_contentComment0_node_creator_User_unique_ignored - } - WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0_contentComment0_node_creator0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this0 } @@ -258,10 +244,10 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { id_EQ: "id-01" } + where: { id: { eq: "id-01" } } update: { content: { - where: { node: { id_EQ: "post-id" } } + where: { node: { id: { eq: "post-id" } } } update: { node: { creator: { update: { node: { id_SET: "not bound" } } } } } } } @@ -279,7 +265,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 WITH this CALL { @@ -298,14 +285,6 @@ describe("Cypher Auth Allow", () => { WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_content0_creator0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN count(*) AS update_this_content0_creator0 } - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Comment @@ -327,18 +306,10 @@ describe("Cypher Auth Allow", () => { RETURN count(*) AS update_this_content0_creator0 } WITH this, this_content0 - OPTIONAL MATCH (this_content0)<-[:HAS_CONTENT]-(authorization__after_this1:User) - WITH *, count(authorization__after_this1) AS authorization__after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__after_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__after_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_content0)<-[:HAS_CONTENT]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Post @@ -367,18 +338,22 @@ describe("Cypher Auth Allow", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"post-id\\" + \\"id\\": { + \\"eq\\": \\"post-id\\" + } } }, \\"update\\": { \\"node\\": { - \\"creator\\": { - \\"update\\": { - \\"node\\": { - \\"id_SET\\": \\"not bound\\" + \\"creator\\": [ + { + \\"update\\": { + \\"node\\": { + \\"id_SET\\": \\"not bound\\" + } } } - } + ] } } } diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/validate/validate.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/validate/validate.test.ts index afa75593a4..a4163e40a3 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/validate/validate.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/validate/validate.test.ts @@ -30,7 +30,7 @@ describe("Cypher Auth Allow", () => { typeDefs = /* GraphQL */ ` type Post @node { id: ID - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } type User @node { @@ -44,7 +44,7 @@ describe("Cypher Auth Allow", () => { validate: { when: AFTER operations: [CREATE, UPDATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { id_EQ: "$jwt.sub" } } + where: { node: { id: { eq: "$jwt.sub" } } } } ) @@ -54,7 +54,7 @@ describe("Cypher Auth Allow", () => { { when: AFTER operations: [CREATE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) @@ -87,7 +87,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -149,7 +150,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) @@ -177,18 +179,11 @@ describe("Cypher Auth Allow", () => { RETURN collect(NULL) AS create_var8 } WITH * - OPTIONAL MATCH (create_this3)<-[:HAS_POST]-(create_this9:User) - WITH *, count(create_this9) AS create_var10 - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (create_var10 <> 0 AND ($jwt.sub IS NOT NULL AND create_this9.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)<-[create_this11:HAS_POST]-(:User) - WITH count(create_this11) AS c - WHERE apoc.util.validatePredicate(NOT (c = 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once\\", [0]) - RETURN c AS create_var12 - } - RETURN collect(NULL) AS create_var13 + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (create_this3)<-[:HAS_POST]-(create_this9:User) + WHERE ($jwt.sub IS NOT NULL AND create_this9.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + RETURN collect(NULL) AS create_var10 } WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this1.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) @@ -209,11 +204,13 @@ describe("Cypher Auth Allow", () => { \\"node\\": { \\"id\\": \\"post-id-1\\", \\"creator\\": { - \\"create\\": { - \\"node\\": { - \\"id\\": \\"some-user-id\\" + \\"create\\": [ + { + \\"node\\": { + \\"id\\": \\"some-user-id\\" + } } - } + ] } } } @@ -235,7 +232,7 @@ describe("Cypher Auth Allow", () => { test("Update Node", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { id_EQ: "id-01" }, update: { id_SET: "not bound" }) { + updateUsers(where: { id: { eq: "id-01" } }, update: { id_SET: "not bound" }) { users { id } @@ -249,7 +246,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 SET this.id = $this_update_id_SET WITH this @@ -277,10 +275,10 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { id_EQ: "id-01" } + where: { id: { eq: "id-01" } } update: { posts: { - where: { node: { id_EQ: "post-id" } } + where: { node: { id: { eq: "post-id" } } } update: { node: { creator: { update: { node: { id_SET: "not bound" } } } } } } } @@ -298,7 +296,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 WITH this CALL { @@ -314,14 +313,6 @@ describe("Cypher Auth Allow", () => { WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_posts0_creator0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN count(*) AS update_this_posts0_creator0 } - WITH this, this_posts0 - CALL { - WITH this_posts0 - MATCH (this_posts0)<-[this_posts0_creator_User_unique:HAS_POST]-(:User) - WITH count(this_posts0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_posts0_creator_User_unique_ignored - } RETURN count(*) AS update_this_posts0 } WITH this @@ -348,18 +339,22 @@ describe("Cypher Auth Allow", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"post-id\\" + \\"id\\": { + \\"eq\\": \\"post-id\\" + } } }, \\"update\\": { \\"node\\": { - \\"creator\\": { - \\"update\\": { - \\"node\\": { - \\"id_SET\\": \\"not bound\\" + \\"creator\\": [ + { + \\"update\\": { + \\"node\\": { + \\"id_SET\\": \\"not bound\\" + } } } - } + ] } } } @@ -376,8 +371,8 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updatePosts( - where: { id_EQ: "post-id" } - update: { creator: { connect: { where: { node: { id_EQ: "user-id" } } } } } + where: { id: { eq: "post-id" } } + update: { creator: { connect: { where: { node: { id: { eq: "user-id" } } } } } } ) { posts { id @@ -392,7 +387,8 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE this.id = $param0 WITH * CALL { @@ -406,25 +402,17 @@ describe("Cypher Auth Allow", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_creator0_connect0_node - MERGE (this)<-[:HAS_POST]-(this_creator0_connect0_node) + CREATE (this)<-[:HAS_POST]-(this_creator0_connect0_node) } } WITH this, this_creator0_connect0_node - WITH * - OPTIONAL MATCH (this)<-[:HAS_POST]-(authorization__after_this1:User) - WITH *, count(authorization__after_this1) AS authorization__after_var0 - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__after_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__after_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_creator0_connect0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this, this_creator0_connect0_node + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_creator0_connect0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this_creator0_connect_User0 } - WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_POST]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } RETURN collect(DISTINCT this { .id }) AS data" `); @@ -448,8 +436,8 @@ describe("Cypher Auth Allow", () => { const query = /* GraphQL */ ` mutation { updatePosts( - where: { id_EQ: "post-id" } - update: { creator: { disconnect: { where: { node: { id_EQ: "user-id" } } } } } + where: { id: { eq: "post-id" } } + update: { creator: { disconnect: { where: { node: { id: { eq: "user-id" } } } } } } ) { posts { id @@ -464,41 +452,34 @@ describe("Cypher Auth Allow", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE this.id = $param0 WITH this CALL { WITH this OPTIONAL MATCH (this)<-[this_creator0_disconnect0_rel:HAS_POST]-(this_creator0_disconnect0:User) - WHERE this_creator0_disconnect0.id = $updatePosts_args_update_creator_disconnect_where_User_this_creator0_disconnect0param0 + WHERE this_creator0_disconnect0.id = $updatePosts_args_update_creator0_disconnect0_where_User_this_creator0_disconnect0param0 CALL { WITH this_creator0_disconnect0, this_creator0_disconnect0_rel, this WITH collect(this_creator0_disconnect0) as this_creator0_disconnect0, this_creator0_disconnect0_rel, this UNWIND this_creator0_disconnect0 as x DELETE this_creator0_disconnect0_rel } - WITH * - OPTIONAL MATCH (this)<-[:HAS_POST]-(authorization__after_this1:User) - WITH *, count(authorization__after_this1) AS authorization__after_var0 - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__after_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__after_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_creator0_disconnect0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this, this_creator0_disconnect0 + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_POST]-(authorization__after_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__after_this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this_creator0_disconnect0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS disconnect_this_creator0_disconnect_User } - WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_POST]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } RETURN collect(DISTINCT this { .id }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": \\"post-id\\", - \\"updatePosts_args_update_creator_disconnect_where_User_this_creator0_disconnect0param0\\": \\"user-id\\", + \\"updatePosts_args_update_creator0_disconnect0_where_User_this_creator0_disconnect0param0\\": \\"user-id\\", \\"isAuthenticated\\": true, \\"jwt\\": { \\"roles\\": [ @@ -509,15 +490,21 @@ describe("Cypher Auth Allow", () => { \\"updatePosts\\": { \\"args\\": { \\"update\\": { - \\"creator\\": { - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"user-id\\" + \\"creator\\": [ + { + \\"disconnect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"user-id\\" + } + } + } } - } + ] } - } + ] } } }, diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/where/connection-auth-filter.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/where/connection-auth-filter.test.ts index 73126acb7d..fa6598ca21 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/where/connection-auth-filter.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/where/connection-auth-filter.test.ts @@ -44,23 +44,26 @@ describe("Connection auth filter", () => { type Post @node { id: ID content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } - extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type User { password: String! - @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) } extend type Post { secretKey: String! @authorization( - filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }] + filter: [ + { operations: [READ], where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } + ] ) } - extend type Post @authorization(filter: [{ where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type Post + @authorization(filter: [{ where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }]) `; neoSchema = new Neo4jGraphQL({ @@ -92,7 +95,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -121,7 +125,7 @@ describe("Connection auth filter", () => { test("Read Node + User Defined Where", async () => { const query = /* GraphQL */ ` { - usersConnection(where: { name_EQ: "bob" }) { + usersConnection(where: { name: { eq: "bob" } }) { edges { node { id @@ -137,7 +141,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE (this0.name = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -186,7 +191,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -197,16 +203,18 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 + WITH DISTINCT this2 WITH * - WHERE ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }) WITH this2 { .content } AS this2 - RETURN collect(this2) AS var5 + RETURN collect(this2) AS var4 } - RETURN collect({ node: { id: this0.id, posts: var5, __resolveType: \\"User\\" } }) AS var6 + RETURN collect({ node: { id: this0.id, posts: var4, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS this" + RETURN { edges: var5, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -248,7 +256,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -259,23 +268,23 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }) WITH collect({ node: this2, relationship: this1 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this2, edge.relationship AS this1 - RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var5 + RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var4 } - RETURN { edges: var5, totalCount: totalCount } AS var6 + RETURN { edges: var4, totalCount: totalCount } AS var5 } - RETURN collect({ node: { id: this0.id, postsConnection: var6, __resolveType: \\"User\\" } }) AS var7 + RETURN collect({ node: { id: this0.id, postsConnection: var5, __resolveType: \\"User\\" } }) AS var6 } - RETURN { edges: var7, totalCount: totalCount } AS this" + RETURN { edges: var6, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -298,7 +307,7 @@ describe("Connection auth filter", () => { edges { node { id - postsConnection(where: { node: { id_EQ: "some-id" } }) { + postsConnection(where: { node: { id: { eq: "some-id" } } }) { edges { node { content @@ -317,7 +326,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -328,23 +338,23 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE (this2.id = $param2 AND ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)))) + WHERE (this2.id = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + })) WITH collect({ node: this2, relationship: this1 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this2, edge.relationship AS this1 - RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var5 + RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var4 } - RETURN { edges: var5, totalCount: totalCount } AS var6 + RETURN { edges: var4, totalCount: totalCount } AS var5 } - RETURN collect({ node: { id: this0.id, postsConnection: var6, __resolveType: \\"User\\" } }) AS var7 + RETURN collect({ node: { id: this0.id, postsConnection: var5, __resolveType: \\"User\\" } }) AS var6 } - RETURN { edges: var7, totalCount: totalCount } AS this" + RETURN { edges: var6, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -368,7 +378,7 @@ describe("Connection auth filter", () => { edges { node { id - posts(where: { content_EQ: "cool" }) { + posts(where: { content: { eq: "cool" } }) { content } } @@ -383,7 +393,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -394,16 +405,18 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 + WITH DISTINCT this2 WITH * - WHERE (this2.content = $param2 AND ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)))) + WHERE (this2.content = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + })) WITH this2 { .content } AS this2 - RETURN collect(this2) AS var5 + RETURN collect(this2) AS var4 } - RETURN collect({ node: { id: this0.id, posts: var5, __resolveType: \\"User\\" } }) AS var6 + RETURN collect({ node: { id: this0.id, posts: var4, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS this" + RETURN { edges: var5, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -444,7 +457,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -457,19 +471,19 @@ describe("Connection auth filter", () => { CALL { WITH * MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }) WITH this2 { .id, __resolveType: \\"Post\\", __id: id(this2) } AS this2 - RETURN this2 AS var5 + RETURN this2 AS var4 } - WITH var5 - RETURN collect(var5) AS var5 + WITH var4 + RETURN collect(var4) AS var4 } - RETURN collect({ node: { id: this0.id, content: var5, __resolveType: \\"User\\" } }) AS var6 + RETURN collect({ node: { id: this0.id, content: var4, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS this" + RETURN { edges: var5, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -513,7 +527,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -526,20 +541,20 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }) WITH { node: { __resolveType: \\"Post\\", __id: id(this2), id: this2.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var5 + RETURN { edges: edges, totalCount: totalCount } AS var4 } - RETURN collect({ node: { id: this0.id, contentConnection: var5, __resolveType: \\"User\\" } }) AS var6 + RETURN collect({ node: { id: this0.id, contentConnection: var4, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS this" + RETURN { edges: var5, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -562,7 +577,7 @@ describe("Connection auth filter", () => { edges { node { id - contentConnection(where: { Post: { node: { id_EQ: "some-id" } } }) { + contentConnection(where: { Post: { node: { id: { eq: "some-id" } } } }) { edges { node { ... on Post { @@ -583,7 +598,8 @@ describe("Connection auth filter", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -596,20 +612,20 @@ describe("Connection auth filter", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE (this2.id = $param2 AND ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)))) + WHERE (this2.id = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + })) WITH { node: { __resolveType: \\"Post\\", __id: id(this2), id: this2.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var5 + RETURN { edges: edges, totalCount: totalCount } AS var4 } - RETURN collect({ node: { id: this0.id, contentConnection: var5, __resolveType: \\"User\\" } }) AS var6 + RETURN collect({ node: { id: this0.id, contentConnection: var4, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS this" + RETURN { edges: var5, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/where/interface-relationships/implementation-where.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/where/interface-relationships/implementation-where.test.ts index f2bfe6219b..6842060176 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/where/interface-relationships/implementation-where.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/where/interface-relationships/implementation-where.test.ts @@ -31,7 +31,7 @@ describe("Cypher Auth Where", () => { interface Content { id: ID content: String - creator: User! @declareRelationship + creator: [User!]! @declareRelationship } type User @node { @@ -43,7 +43,7 @@ describe("Cypher Auth Where", () => { type Comment implements Content @node { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) } type Post implements Content @@ -52,13 +52,13 @@ describe("Cypher Auth Where", () => { filter: [ { operations: [READ, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { creator: { id_EQ: "$jwt.sub" } } } + where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } ] ) { id: ID content: String - creator: User! @relationship(type: "HAS_CONTENT", direction: IN) + creator: [User!]! @relationship(type: "HAS_CONTENT", direction: IN) } extend type User @@ -66,20 +66,22 @@ describe("Cypher Auth Where", () => { filter: [ { operations: [READ, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { node: { id_EQ: "$jwt.sub" } } + where: { node: { id: { eq: "$jwt.sub" } } } } ] ) extend type User { password: String! - @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) } extend type Post { secretKey: String! @authorization( - filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }] + filter: [ + { operations: [READ], where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } + ] ) } `; @@ -107,11 +109,13 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }) RETURN this { .id } AS this" `); @@ -131,7 +135,7 @@ describe("Cypher Auth Where", () => { test("Read Node + User Defined Where", async () => { const query = /* GraphQL */ ` { - posts(where: { content_EQ: "bob" }) { + posts(where: { content: { eq: "bob" } }) { id } } @@ -141,11 +145,13 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE (this.content = $param0 AND ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)))) + WHERE (this.content = $param0 AND ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + })) RETURN this { .id } AS this" `); @@ -181,7 +187,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -194,10 +201,10 @@ describe("Cypher Auth Where", () => { UNION WITH * MATCH (this)-[this3:HAS_CONTENT]->(this4:Post) - OPTIONAL MATCH (this4)<-[:HAS_CONTENT]-(this5:User) - WITH *, count(this5) AS var6 - WITH * - WHERE ($isAuthenticated = true AND (var6 <> 0 AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this4)<-[:HAS_CONTENT]-(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub) + }) WITH this4 { .id, __resolveType: \\"Post\\", __id: id(this4) } AS this4 RETURN this4 AS var2 } @@ -242,7 +249,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -255,18 +263,18 @@ describe("Cypher Auth Where", () => { UNION WITH this MATCH (this)-[this2:HAS_CONTENT]->(this3:Post) - OPTIONAL MATCH (this3)<-[:HAS_CONTENT]-(this4:User) - WITH *, count(this4) AS var5 - WITH * - WHERE ($isAuthenticated = true AND (var5 <> 0 AND ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this3)<-[:HAS_CONTENT]-(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub) + }) WITH { node: { __resolveType: \\"Post\\", __id: id(this3), id: this3.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var6 + RETURN { edges: edges, totalCount: totalCount } AS var5 } - RETURN this { .id, contentConnection: var6 } AS this" + RETURN this { .id, contentConnection: var5 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -287,7 +295,7 @@ describe("Cypher Auth Where", () => { { users { id - contentConnection(where: { node: { id_EQ: "some-id" } }) { + contentConnection(where: { node: { id: { eq: "some-id" } } }) { edges { node { ... on Post { @@ -304,7 +312,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -318,18 +327,18 @@ describe("Cypher Auth Where", () => { UNION WITH this MATCH (this)-[this2:HAS_CONTENT]->(this3:Post) - OPTIONAL MATCH (this3)<-[:HAS_CONTENT]-(this4:User) - WITH *, count(this4) AS var5 - WITH * - WHERE (this3.id = $param3 AND ($isAuthenticated = true AND (var5 <> 0 AND ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub)))) + WHERE (this3.id = $param3 AND ($isAuthenticated = true AND EXISTS { + MATCH (this3)<-[:HAS_CONTENT]-(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub) + })) WITH { node: { __resolveType: \\"Post\\", __id: id(this3), id: this3.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var6 + RETURN { edges: edges, totalCount: totalCount } AS var5 } - RETURN this { .id, contentConnection: var6 } AS this" + RETURN this { .id, contentConnection: var5 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -362,24 +371,19 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }) SET this.content = $this_update_content_SET WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(update_this0:User) - WITH *, count(update_this0) AS update_var1 - WITH * - WHERE ($isAuthenticated = true AND (update_var1 <> 0 AND ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(update_this0:User) + WHERE ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub) + }) RETURN collect(DISTINCT this { .id }) AS data" `); @@ -401,7 +405,7 @@ describe("Cypher Auth Where", () => { test("Update Node + User Defined Where", async () => { const query = /* GraphQL */ ` mutation { - updatePosts(where: { content_EQ: "bob" }, update: { content_SET: "Bob" }) { + updatePosts(where: { content: { eq: "bob" } }, update: { content_SET: "Bob" }) { posts { id } @@ -413,24 +417,19 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Post) WITH * - WHERE (this.content = $param0 AND ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)))) + WHERE (this.content = $param0 AND ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + })) SET this.content = $this_update_content_SET WITH * - CALL { - WITH this - MATCH (this)<-[this_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_creator_User_unique_ignored - } - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(update_this0:User) - WITH *, count(update_this0) AS update_var1 - WITH * - WHERE ($isAuthenticated = true AND (update_var1 <> 0 AND ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(update_this0:User) + WHERE ($jwt.sub IS NOT NULL AND update_this0.id = $jwt.sub) + }) RETURN collect(DISTINCT this { .id }) AS data" `); @@ -465,7 +464,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this @@ -476,14 +476,6 @@ describe("Cypher Auth Where", () => { WITH this MATCH (this)-[this_has_content0_relationship:HAS_CONTENT]->(this_content0:Comment) SET this_content0.id = $this_update_content0_id_SET - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDComment.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Comment @@ -494,18 +486,11 @@ describe("Cypher Auth Where", () => { CALL { WITH this MATCH (this)-[this_has_content0_relationship:HAS_CONTENT]->(this_content0:Post) - OPTIONAL MATCH (this_content0)<-[:HAS_CONTENT]-(authorization_updatebefore_this1:User) - WITH *, count(authorization_updatebefore_this1) AS authorization_updatebefore_var0 - WHERE ($isAuthenticated = true AND (authorization_updatebefore_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_updatebefore_this1.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this_content0)<-[:HAS_CONTENT]-(authorization_updatebefore_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_updatebefore_this0.id = $jwt.sub) + }) SET this_content0.id = $this_update_content0_id_SET - WITH this, this_content0 - CALL { - WITH this_content0 - MATCH (this_content0)<-[this_content0_creator_User_unique:HAS_CONTENT]-(:User) - WITH count(this_content0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_content0_creator_User_unique_ignored - } RETURN count(*) AS update_this_content0 } RETURN count(*) AS update_this_Post @@ -543,10 +528,12 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 - WHERE ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))) + "CYPHER 5 + MATCH (this:Post) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }) DETACH DELETE this" `); @@ -566,7 +553,7 @@ describe("Cypher Auth Where", () => { test("Delete Node + User Defined Where", async () => { const query = /* GraphQL */ ` mutation { - deletePosts(where: { content_EQ: "Bob" }) { + deletePosts(where: { content: { eq: "Bob" } }) { nodesDeleted } } @@ -576,10 +563,12 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - OPTIONAL MATCH (this)<-[:HAS_CONTENT]-(this0:User) - WITH *, count(this0) AS var1 - WHERE (this.content = $param0 AND ($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)))) + "CYPHER 5 + MATCH (this:Post) + WHERE (this.content = $param0 AND ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:HAS_CONTENT]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + })) DETACH DELETE this" `); @@ -610,7 +599,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH * CALL { @@ -626,14 +616,15 @@ describe("Cypher Auth Where", () => { CALL { WITH * OPTIONAL MATCH (this)-[this4:HAS_CONTENT]->(this5:Post) - OPTIONAL MATCH (this5)<-[:HAS_CONTENT]-(this6:User) - WITH *, count(this6) AS var7 - WHERE ($isAuthenticated = true AND (var7 <> 0 AND ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub))) - WITH this4, collect(DISTINCT this5) AS var8 + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this5)<-[:HAS_CONTENT]-(this6:User) + WHERE ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub) + }) + WITH this4, collect(DISTINCT this5) AS var7 CALL { - WITH var8 - UNWIND var8 AS var9 - DETACH DELETE var9 + WITH var7 + UNWIND var7 AS var8 + DETACH DELETE var8 } } WITH * @@ -672,7 +663,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -688,7 +680,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_content_connect0_node - MERGE (this0)-[:HAS_CONTENT]->(this0_content_connect0_node) + CREATE (this0)-[:HAS_CONTENT]->(this0_content_connect0_node) } } WITH this0, this0_content_connect0_node @@ -697,10 +689,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this0 OPTIONAL MATCH (this0_content_connect1_node:Post) - OPTIONAL MATCH (this0_content_connect1_node)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this0_content_connect1_node)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) CALL { WITH * WITH collect(this0_content_connect1_node) as connectedNodes, collect(this0) as parentNodes @@ -708,7 +700,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_content_connect1_node - MERGE (this0)-[:HAS_CONTENT]->(this0_content_connect1_node) + CREATE (this0)-[:HAS_CONTENT]->(this0_content_connect1_node) } } WITH this0, this0_content_connect1_node @@ -749,7 +741,7 @@ describe("Cypher Auth Where", () => { id: "123" name: "Bob" password: "password" - content: { connect: { where: { node: { id_EQ: "post-id" } } } } + content: { connect: { where: { node: { id: { eq: "post-id" } } } } } } ] ) { @@ -764,7 +756,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -781,7 +774,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_content_connect0_node - MERGE (this0)-[:HAS_CONTENT]->(this0_content_connect0_node) + CREATE (this0)-[:HAS_CONTENT]->(this0_content_connect0_node) } } WITH this0, this0_content_connect0_node @@ -790,10 +783,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this0 OPTIONAL MATCH (this0_content_connect1_node:Post) - OPTIONAL MATCH (this0_content_connect1_node)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this0_content_connect1_node.id = $this0_content_connect1_node_param0 AND ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) + WHERE this0_content_connect1_node.id = $this0_content_connect1_node_param0 AND ($isAuthenticated = true AND EXISTS { + MATCH (this0_content_connect1_node)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) CALL { WITH * WITH collect(this0_content_connect1_node) as connectedNodes, collect(this0) as parentNodes @@ -801,7 +794,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_content_connect1_node - MERGE (this0)-[:HAS_CONTENT]->(this0_content_connect1_node) + CREATE (this0)-[:HAS_CONTENT]->(this0_content_connect1_node) } } WITH this0, this0_content_connect1_node @@ -850,7 +843,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this @@ -868,7 +862,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_content0_connect0_node - MERGE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) + CREATE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) } } WITH this, this_content0_connect0_node @@ -882,10 +876,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this OPTIONAL MATCH (this_content0_connect0_node:Post) - OPTIONAL MATCH (this_content0_connect0_node)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE (($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) + WHERE (($isAuthenticated = true AND EXISTS { + MATCH (this_content0_connect0_node)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) CALL { WITH * WITH collect(this_content0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -893,7 +887,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_content0_connect0_node - MERGE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) + CREATE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) } } WITH this, this_content0_connect0_node @@ -923,7 +917,7 @@ describe("Cypher Auth Where", () => { test("Connect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { content: { connect: { where: { node: { id_EQ: "new-id" } } } } }) { + updateUsers(update: { content: { connect: { where: { node: { id: { eq: "new-id" } } } } } }) { users { id } @@ -935,7 +929,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this @@ -953,7 +948,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_content0_connect0_node - MERGE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) + CREATE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) } } WITH this, this_content0_connect0_node @@ -967,10 +962,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this OPTIONAL MATCH (this_content0_connect0_node:Post) - OPTIONAL MATCH (this_content0_connect0_node)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_content0_connect0_node.id = $this_content0_connect0_node_param0 AND (($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) + WHERE this_content0_connect0_node.id = $this_content0_connect0_node_param0 AND (($isAuthenticated = true AND EXISTS { + MATCH (this_content0_connect0_node)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) CALL { WITH * WITH collect(this_content0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -978,7 +973,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_content0_connect0_node - MERGE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) + CREATE (this)-[:HAS_CONTENT]->(this_content0_connect0_node) } } WITH this, this_content0_connect0_node @@ -1021,7 +1016,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this @@ -1048,10 +1044,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this OPTIONAL MATCH (this)-[this_content0_disconnect0_rel:HAS_CONTENT]->(this_content0_disconnect0:Post) - OPTIONAL MATCH (this_content0_disconnect0)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub)))) + WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND EXISTS { + MATCH (this_content0_disconnect0)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + })) CALL { WITH this_content0_disconnect0, this_content0_disconnect0_rel, this WITH collect(this_content0_disconnect0) as this_content0_disconnect0, this_content0_disconnect0_rel, this @@ -1084,7 +1080,7 @@ describe("Cypher Auth Where", () => { test("Disconnect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { content: [{ disconnect: { where: { node: { id_EQ: "new-id" } } } }] }) { + updateUsers(update: { content: [{ disconnect: { where: { node: { id: { eq: "new-id" } } } } }] }) { users { id } @@ -1096,7 +1092,8 @@ describe("Cypher Auth Where", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this @@ -1123,10 +1120,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this OPTIONAL MATCH (this)-[this_content0_disconnect0_rel:HAS_CONTENT]->(this_content0_disconnect0:Post) - OPTIONAL MATCH (this_content0_disconnect0)<-[:HAS_CONTENT]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_content0_disconnect0.id = $updateUsers_args_update_content0_disconnect0_where_Post_this_content0_disconnect0param0 AND (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub)))) + WHERE this_content0_disconnect0.id = $updateUsers_args_update_content0_disconnect0_where_Post_this_content0_disconnect0param0 AND (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND EXISTS { + MATCH (this_content0_disconnect0)<-[:HAS_CONTENT]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + })) CALL { WITH this_content0_disconnect0, this_content0_disconnect0_rel, this WITH collect(this_content0_disconnect0) as this_content0_disconnect0, this_content0_disconnect0_rel, this @@ -1162,7 +1159,9 @@ describe("Cypher Auth Where", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"new-id\\" + \\"id\\": { + \\"eq\\": \\"new-id\\" + } } } } diff --git a/packages/graphql/tests/tck/directives/authorization/arguments/where/where.test.ts b/packages/graphql/tests/tck/directives/authorization/arguments/where/where.test.ts index afdbeb19ed..3dc00c9e99 100644 --- a/packages/graphql/tests/tck/directives/authorization/arguments/where/where.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/arguments/where/where.test.ts @@ -44,23 +44,26 @@ describe("Cypher Auth Where", () => { type Post @node { id: ID content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } - extend type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(filter: [{ where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type User { password: String! - @authorization(filter: [{ operations: [READ], where: { node: { id_EQ: "$jwt.sub" } } }]) + @authorization(filter: [{ operations: [READ], where: { node: { id: { eq: "$jwt.sub" } } } }]) } extend type Post { secretKey: String! @authorization( - filter: [{ operations: [READ], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }] + filter: [ + { operations: [READ], where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } } + ] ) } - extend type Post @authorization(filter: [{ where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + extend type Post + @authorization(filter: [{ where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }]) `; neoSchema = new Neo4jGraphQL({ @@ -88,7 +91,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) RETURN this { .id } AS this" @@ -110,7 +114,7 @@ describe("Cypher Auth Where", () => { test("Read Node + User Defined Where", async () => { const query = /* GraphQL */ ` { - users(where: { name_EQ: "bob" }) { + users(where: { name: { eq: "bob" } }) { id } } @@ -122,7 +126,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.name = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) RETURN this { .id } AS this" @@ -160,20 +165,23 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 + WITH DISTINCT this1 WITH * - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }) WITH this1 { .content } AS this1 - RETURN collect(this1) AS var4 + RETURN collect(this1) AS var3 } - RETURN this { .id, posts: var4 } AS this" + RETURN this { .id, posts: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -211,27 +219,28 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var4 + RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var3 } - RETURN { edges: var4, totalCount: totalCount } AS var5 + RETURN { edges: var3, totalCount: totalCount } AS var4 } - RETURN this { .id, postsConnection: var5 } AS this" + RETURN this { .id, postsConnection: var4 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -252,7 +261,7 @@ describe("Cypher Auth Where", () => { { users { id - postsConnection(where: { node: { id_EQ: "some-id" } }) { + postsConnection(where: { node: { id: { eq: "some-id" } } }) { edges { node { content @@ -269,27 +278,28 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE (this1.id = $param2 AND ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)))) + WHERE (this1.id = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + })) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var4 + RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var3 } - RETURN { edges: var4, totalCount: totalCount } AS var5 + RETURN { edges: var3, totalCount: totalCount } AS var4 } - RETURN this { .id, postsConnection: var5 } AS this" + RETURN this { .id, postsConnection: var4 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -311,7 +321,7 @@ describe("Cypher Auth Where", () => { { users { id - posts(where: { content_EQ: "cool" }) { + posts(where: { content: { eq: "cool" } }) { content } } @@ -324,20 +334,23 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 + WITH DISTINCT this1 WITH * - WHERE (this1.content = $param2 AND ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)))) + WHERE (this1.content = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + })) WITH this1 { .content } AS this1 - RETURN collect(this1) AS var4 + RETURN collect(this1) AS var3 } - RETURN this { .id, posts: var4 } AS this" + RETURN this { .id, posts: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -374,7 +387,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -382,17 +396,17 @@ describe("Cypher Auth Where", () => { CALL { WITH * MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }) WITH this1 { .id, __resolveType: \\"Post\\", __id: id(this1) } AS this1 - RETURN this1 AS var4 + RETURN this1 AS var3 } - WITH var4 - RETURN collect(var4) AS var4 + WITH var3 + RETURN collect(var3) AS var3 } - RETURN this { .id, content: var4 } AS this" + RETURN this { .id, content: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -432,7 +446,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -440,18 +455,18 @@ describe("Cypher Auth Where", () => { CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }) WITH { node: { __resolveType: \\"Post\\", __id: id(this1), id: this1.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var4 + RETURN { edges: edges, totalCount: totalCount } AS var3 } - RETURN this { .id, contentConnection: var4 } AS this" + RETURN this { .id, contentConnection: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -472,7 +487,7 @@ describe("Cypher Auth Where", () => { { users { id - contentConnection(where: { Post: { node: { id_EQ: "some-id" } } }) { + contentConnection(where: { Post: { node: { id: { eq: "some-id" } } } }) { edges { node { ... on Post { @@ -491,7 +506,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) CALL { @@ -499,18 +515,18 @@ describe("Cypher Auth Where", () => { CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE (this1.id = $param2 AND ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)))) + WHERE (this1.id = $param2 AND ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + })) WITH { node: { __resolveType: \\"Post\\", __id: id(this1), id: this1.id } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var4 + RETURN { edges: edges, totalCount: totalCount } AS var3 } - RETURN this { .id, contentConnection: var4 } AS this" + RETURN this { .id, contentConnection: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -544,7 +560,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) SET this.name = $this_update_name_SET @@ -571,7 +588,7 @@ describe("Cypher Auth Where", () => { test("Update Node + User Defined Where", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { name_EQ: "bob" }, update: { name_SET: "Bob" }) { + updateUsers(where: { name: { eq: "bob" } }, update: { name_SET: "Bob" }) { users { id } @@ -585,7 +602,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.name = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) SET this.name = $this_update_name_SET @@ -630,25 +648,19 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this CALL { WITH this MATCH (this)-[this_has_post0_relationship:HAS_POST]->(this_posts0:Post) - OPTIONAL MATCH (this_posts0)<-[:HAS_POST]-(authorization_updatebefore_this1:User) - WITH *, count(authorization_updatebefore_this1) AS authorization_updatebefore_var0 - WHERE ($isAuthenticated = true AND (authorization_updatebefore_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_updatebefore_this1.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this_posts0)<-[:HAS_POST]-(authorization_updatebefore_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_updatebefore_this0.id = $jwt.sub) + }) SET this_posts0.id = $this_update_posts0_id_SET - WITH this, this_posts0 - CALL { - WITH this_posts0 - MATCH (this_posts0)<-[this_posts0_creator_User_unique:HAS_POST]-(:User) - WITH count(this_posts0_creator_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.creator required exactly once', [0]) - RETURN c AS this_posts0_creator_User_unique_ignored - } RETURN count(*) AS update_this_posts0 } WITH * @@ -656,14 +668,16 @@ describe("Cypher Auth Where", () => { CALL { WITH this MATCH (this)-[update_this0:HAS_POST]->(update_this1:Post) - OPTIONAL MATCH (update_this1)<-[:HAS_POST]-(update_this2:User) - WITH *, count(update_this2) AS update_var3 + WITH DISTINCT update_this1 WITH * - WHERE ($isAuthenticated = true AND (update_var3 <> 0 AND ($jwt.sub IS NOT NULL AND update_this2.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (update_this1)<-[:HAS_POST]-(update_this2:User) + WHERE ($jwt.sub IS NOT NULL AND update_this2.id = $jwt.sub) + }) WITH update_this1 { .id } AS update_this1 - RETURN collect(update_this1) AS update_var4 + RETURN collect(update_this1) AS update_var3 } - RETURN collect(DISTINCT this { .id, posts: update_var4 }) AS data" + RETURN collect(DISTINCT this { .id, posts: update_var3 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -696,7 +710,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) DETACH DELETE this" `); @@ -717,7 +732,7 @@ describe("Cypher Auth Where", () => { test("Delete Node + User Defined Where", async () => { const query = /* GraphQL */ ` mutation { - deleteUsers(where: { name_EQ: "Bob" }) { + deleteUsers(where: { name: { eq: "Bob" } }) { nodesDeleted } } @@ -729,7 +744,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE (this.name = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) DETACH DELETE this" `); @@ -763,20 +779,22 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH * CALL { WITH * OPTIONAL MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))) - WITH this0, collect(DISTINCT this1) AS var4 + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }) + WITH this0, collect(DISTINCT this1) AS var3 CALL { - WITH var4 - UNWIND var4 AS var5 - DETACH DELETE var5 + WITH var3 + UNWIND var3 AS var4 + DETACH DELETE var4 } } WITH * @@ -817,7 +835,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -826,10 +845,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this0 OPTIONAL MATCH (this0_posts_connect0_node:Post) - OPTIONAL MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this1:User) - WITH *, count(authorization_0_before_this1) AS authorization_0_before_var0 - WITH * - WHERE ($isAuthenticated = true AND (authorization_0_before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_0_before_this1.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) + }) CALL { WITH * WITH collect(this0_posts_connect0_node) as connectedNodes, collect(this0) as parentNodes @@ -837,7 +856,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_posts_connect0_node - MERGE (this0)-[:HAS_POST]->(this0_posts_connect0_node) + CREATE (this0)-[:HAS_POST]->(this0_posts_connect0_node) } } WITH this0, this0_posts_connect0_node @@ -878,7 +897,7 @@ describe("Cypher Auth Where", () => { id: "123" name: "Bob" password: "password" - posts: { connect: { where: { node: { id_EQ: "post-id" } } } } + posts: { connect: { where: { node: { id: { eq: "post-id" } } } } } } ] ) { @@ -895,7 +914,8 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:User) SET this0.id = $this0_id SET this0.name = $this0_name @@ -904,10 +924,10 @@ describe("Cypher Auth Where", () => { CALL { WITH this0 OPTIONAL MATCH (this0_posts_connect0_node:Post) - OPTIONAL MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this1:User) - WITH *, count(authorization_0_before_this1) AS authorization_0_before_var0 - WITH * - WHERE this0_posts_connect0_node.id = $this0_posts_connect0_node_param0 AND ($isAuthenticated = true AND (authorization_0_before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_0_before_this1.id = $jwt.sub))) + WHERE this0_posts_connect0_node.id = $this0_posts_connect0_node_param0 AND ($isAuthenticated = true AND EXISTS { + MATCH (this0_posts_connect0_node)<-[:HAS_POST]-(authorization_0_before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization_0_before_this0.id = $jwt.sub) + }) CALL { WITH * WITH collect(this0_posts_connect0_node) as connectedNodes, collect(this0) as parentNodes @@ -915,7 +935,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_posts_connect0_node - MERGE (this0)-[:HAS_POST]->(this0_posts_connect0_node) + CREATE (this0)-[:HAS_POST]->(this0_posts_connect0_node) } } WITH this0, this0_posts_connect0_node @@ -965,17 +985,18 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH * CALL { WITH this OPTIONAL MATCH (this_posts0_connect0_node:Post) - OPTIONAL MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE (($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) + WHERE (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) CALL { WITH * WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -983,7 +1004,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node @@ -1011,7 +1032,7 @@ describe("Cypher Auth Where", () => { test("Connect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { posts: { connect: { where: { node: { id_EQ: "new-id" } } } } }) { + updateUsers(update: { posts: { connect: { where: { node: { id: { eq: "new-id" } } } } } }) { users { id } @@ -1025,17 +1046,18 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH * CALL { WITH this OPTIONAL MATCH (this_posts0_connect0_node:Post) - OPTIONAL MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub))) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) + WHERE this_posts0_connect0_node.id = $this_posts0_connect0_node_param0 AND (($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_connect0_node)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + }) AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) CALL { WITH * WITH collect(this_posts0_connect0_node) as connectedNodes, collect(this) as parentNodes @@ -1043,7 +1065,7 @@ describe("Cypher Auth Where", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_posts0_connect0_node - MERGE (this)-[:HAS_POST]->(this_posts0_connect0_node) + CREATE (this)-[:HAS_POST]->(this_posts0_connect0_node) } } WITH this, this_posts0_connect0_node @@ -1086,17 +1108,18 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) - OPTIONAL MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub)))) + WHERE (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + })) CALL { WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this @@ -1127,7 +1150,7 @@ describe("Cypher Auth Where", () => { test("Disconnect Node + User Defined Where (from update update)", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(update: { posts: [{ disconnect: { where: { node: { id_EQ: "new-id" } } } }] }) { + updateUsers(update: { posts: [{ disconnect: { where: { node: { id: { eq: "new-id" } } } } }] }) { users { id } @@ -1141,17 +1164,18 @@ describe("Cypher Auth Where", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_posts0_disconnect0_rel:HAS_POST]->(this_posts0_disconnect0:Post) - OPTIONAL MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this1:User) - WITH *, count(authorization__before_this1) AS authorization__before_var0 - WITH * - WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND (authorization__before_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this1.id = $jwt.sub)))) + WHERE this_posts0_disconnect0.id = $updateUsers_args_update_posts0_disconnect0_where_Post_this_posts0_disconnect0param0 AND (($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)) AND ($isAuthenticated = true AND EXISTS { + MATCH (this_posts0_disconnect0)<-[:HAS_POST]-(authorization__before_this0:User) + WHERE ($jwt.sub IS NOT NULL AND authorization__before_this0.id = $jwt.sub) + })) CALL { WITH this_posts0_disconnect0, this_posts0_disconnect0_rel, this WITH collect(this_posts0_disconnect0) as this_posts0_disconnect0, this_posts0_disconnect0_rel, this @@ -1184,7 +1208,9 @@ describe("Cypher Auth Where", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"new-id\\" + \\"id\\": { + \\"eq\\": \\"new-id\\" + } } } } diff --git a/packages/graphql/tests/tck/directives/authorization/projection-connection-union.test.ts b/packages/graphql/tests/tck/directives/authorization/projection-connection-union.test.ts index 0deb258fb2..6d09eb69dd 100644 --- a/packages/graphql/tests/tck/directives/authorization/projection-connection-union.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/projection-connection-union.test.ts @@ -30,7 +30,7 @@ describe("Cypher Auth Projection On Connections On Unions", () => { typeDefs = /* GraphQL */ ` type Post @node { content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } type User @node { @@ -41,9 +41,11 @@ describe("Cypher Auth Projection On Connections On Unions", () => { union Content = Post - extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type Post - @authorization(validate: [{ when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }] + ) `; neoSchema = new Neo4jGraphQL({ @@ -86,7 +88,8 @@ describe("Cypher Auth Projection On Connections On Unions", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { @@ -94,32 +97,32 @@ describe("Cypher Auth Projection On Connections On Unions", () => { CALL { WITH this MATCH (this)-[this0:PUBLISHED]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this1 - MATCH (this1)<-[this4:HAS_POST]-(this5:User) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this5, relationship: this4 }) AS edges + MATCH (this1)<-[this3:HAS_POST]-(this4:User) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH collect({ node: this4, relationship: this3 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge - WITH edge.node AS this5, edge.relationship AS this4 - RETURN collect({ node: { name: this5.name, __resolveType: \\"User\\" } }) AS var6 + WITH edge.node AS this4, edge.relationship AS this3 + RETURN collect({ node: { name: this4.name, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS var7 + RETURN { edges: var5, totalCount: totalCount } AS var6 } - WITH { node: { __resolveType: \\"Post\\", __id: id(this1), content: this1.content, creatorConnection: var7 } } AS edge + WITH { node: { __resolveType: \\"Post\\", __id: id(this1), content: this1.content, creatorConnection: var6 } } AS edge RETURN edge } WITH collect(edge) AS edges WITH edges, size(edges) AS totalCount - RETURN { edges: edges, totalCount: totalCount } AS var8 + RETURN { edges: edges, totalCount: totalCount } AS var7 } - RETURN this { contentConnection: var8 } AS this" + RETURN this { contentConnection: var7 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/directives/authorization/projection-connection.test.ts b/packages/graphql/tests/tck/directives/authorization/projection-connection.test.ts index ee211f36b8..6e4e026a32 100644 --- a/packages/graphql/tests/tck/directives/authorization/projection-connection.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/projection-connection.test.ts @@ -30,7 +30,7 @@ describe("Cypher Auth Projection On Connections", () => { typeDefs = /* GraphQL */ ` type Post @node { content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } type User @node { @@ -39,9 +39,11 @@ describe("Cypher Auth Projection On Connections", () => { posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type Post - @authorization(validate: [{ when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }] + ) `; neoSchema = new Neo4jGraphQL({ @@ -76,27 +78,28 @@ describe("Cypher Auth Projection On Connections", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var4 + RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var3 } - RETURN { edges: var4, totalCount: totalCount } AS var5 + RETURN { edges: var3, totalCount: totalCount } AS var4 } - RETURN this { .name, postsConnection: var5 } AS this" + RETURN this { .name, postsConnection: var4 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -139,16 +142,17 @@ describe("Cypher Auth Projection On Connections", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Post) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -157,23 +161,23 @@ describe("Cypher Auth Projection On Connections", () => { WITH edge.node AS this1, edge.relationship AS this0 CALL { WITH this1 - MATCH (this1)<-[this4:HAS_POST]-(this5:User) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this5, relationship: this4 }) AS edges + MATCH (this1)<-[this3:HAS_POST]-(this4:User) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this4.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH collect({ node: this4, relationship: this3 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge - WITH edge.node AS this5, edge.relationship AS this4 - RETURN collect({ node: { name: this5.name, __resolveType: \\"User\\" } }) AS var6 + WITH edge.node AS this4, edge.relationship AS this3 + RETURN collect({ node: { name: this4.name, __resolveType: \\"User\\" } }) AS var5 } - RETURN { edges: var6, totalCount: totalCount } AS var7 + RETURN { edges: var5, totalCount: totalCount } AS var6 } - RETURN collect({ node: { content: this1.content, creatorConnection: var7, __resolveType: \\"Post\\" } }) AS var8 + RETURN collect({ node: { content: this1.content, creatorConnection: var6, __resolveType: \\"Post\\" } }) AS var7 } - RETURN { edges: var8, totalCount: totalCount } AS var9 + RETURN { edges: var7, totalCount: totalCount } AS var8 } - RETURN this { .name, postsConnection: var9 } AS this" + RETURN this { .name, postsConnection: var8 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -197,7 +201,7 @@ describe("Cypher Auth Projection On top-level connections", () => { typeDefs = /* GraphQL */ ` type Post @node { content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } type User @node { @@ -206,9 +210,11 @@ describe("Cypher Auth Projection On top-level connections", () => { posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(validate: [{ when: BEFORE, where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type Post - @authorization(validate: [{ when: BEFORE, where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: BEFORE, where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }] + ) `; neoSchema = new Neo4jGraphQL({ @@ -247,7 +253,8 @@ describe("Cypher Auth Projection On top-level connections", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -258,23 +265,23 @@ describe("Cypher Auth Projection On top-level connections", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this2, relationship: this1 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this2, edge.relationship AS this1 - RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var5 + RETURN collect({ node: { content: this2.content, __resolveType: \\"Post\\" } }) AS var4 } - RETURN { edges: var5, totalCount: totalCount } AS var6 + RETURN { edges: var4, totalCount: totalCount } AS var5 } - RETURN collect({ node: { name: this0.name, postsConnection: var6, __resolveType: \\"User\\" } }) AS var7 + RETURN collect({ node: { name: this0.name, postsConnection: var5, __resolveType: \\"User\\" } }) AS var6 } - RETURN { edges: var7, totalCount: totalCount } AS this" + RETURN { edges: var6, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -321,7 +328,8 @@ describe("Cypher Auth Projection On top-level connections", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:User) + "CYPHER 5 + MATCH (this0:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -332,10 +340,10 @@ describe("Cypher Auth Projection On top-level connections", () => { CALL { WITH this0 MATCH (this0)-[this1:HAS_POST]->(this2:Post) - OPTIONAL MATCH (this2)<-[:HAS_POST]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this2)<-[:HAS_POST]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this2, relationship: this1 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -344,25 +352,25 @@ describe("Cypher Auth Projection On top-level connections", () => { WITH edge.node AS this2, edge.relationship AS this1 CALL { WITH this2 - MATCH (this2)<-[this5:HAS_POST]-(this6:User) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this6.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this6, relationship: this5 }) AS edges + MATCH (this2)<-[this4:HAS_POST]-(this5:User) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH collect({ node: this5, relationship: this4 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge - WITH edge.node AS this6, edge.relationship AS this5 - RETURN collect({ node: { name: this6.name, __resolveType: \\"User\\" } }) AS var7 + WITH edge.node AS this5, edge.relationship AS this4 + RETURN collect({ node: { name: this5.name, __resolveType: \\"User\\" } }) AS var6 } - RETURN { edges: var7, totalCount: totalCount } AS var8 + RETURN { edges: var6, totalCount: totalCount } AS var7 } - RETURN collect({ node: { content: this2.content, creatorConnection: var8, __resolveType: \\"Post\\" } }) AS var9 + RETURN collect({ node: { content: this2.content, creatorConnection: var7, __resolveType: \\"Post\\" } }) AS var8 } - RETURN { edges: var9, totalCount: totalCount } AS var10 + RETURN { edges: var8, totalCount: totalCount } AS var9 } - RETURN collect({ node: { name: this0.name, postsConnection: var10, __resolveType: \\"User\\" } }) AS var11 + RETURN collect({ node: { name: this0.name, postsConnection: var9, __resolveType: \\"User\\" } }) AS var10 } - RETURN { edges: var11, totalCount: totalCount } AS this" + RETURN { edges: var10, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/directives/authorization/projection-interface-relationships.test.ts b/packages/graphql/tests/tck/directives/authorization/projection-interface-relationships.test.ts index 395d889d91..7c698138cc 100644 --- a/packages/graphql/tests/tck/directives/authorization/projection-interface-relationships.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/projection-interface-relationships.test.ts @@ -42,7 +42,8 @@ describe("Auth projections for interface relationship fields", () => { episodes: String! } - extend type Series @authorization(validate: [{ when: BEFORE, where: { node: { episodes_EQ: "$jwt.sub" } } }]) + extend type Series + @authorization(validate: [{ when: BEFORE, where: { node: { episodes: { eq: "$jwt.sub" } } } }]) type ActedIn @relationshipProperties { screenTime: Int! @@ -87,7 +88,8 @@ describe("Auth projections for interface relationship fields", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/authorization/projection.test.ts b/packages/graphql/tests/tck/directives/authorization/projection.test.ts index 8e63813949..ef50a2fb3c 100644 --- a/packages/graphql/tests/tck/directives/authorization/projection.test.ts +++ b/packages/graphql/tests/tck/directives/authorization/projection.test.ts @@ -34,7 +34,7 @@ describe("Cypher Auth Projection", () => { } extend type User { - id: ID @authorization(validate: [{ when: BEFORE, where: { node: { id_EQ: "$jwt.sub" } } }]) + id: ID @authorization(validate: [{ when: BEFORE, where: { node: { id: { eq: "$jwt.sub" } } } }]) } `; @@ -65,7 +65,8 @@ describe("Cypher Auth Projection", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH this WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) SET this.id = $this_update_id_SET @@ -106,7 +107,8 @@ describe("Cypher Auth Projection", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:User) diff --git a/packages/graphql/tests/tck/directives/autogenerate.test.ts b/packages/graphql/tests/tck/directives/autogenerate.test.ts index 839bd94447..572c17626f 100644 --- a/packages/graphql/tests/tck/directives/autogenerate.test.ts +++ b/packages/graphql/tests/tck/directives/autogenerate.test.ts @@ -27,7 +27,7 @@ describe("Cypher autogenerate directive", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Movie @node { - id: ID! @id @unique + id: ID! @id name: String! } `; @@ -52,7 +52,8 @@ describe("Cypher autogenerate directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -90,7 +91,8 @@ describe("Cypher autogenerate directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) SET this.name = $this_update_name_SET RETURN collect(DISTINCT this { .id, .name }) AS data" `); diff --git a/packages/graphql/tests/tck/directives/coalesce.test.ts b/packages/graphql/tests/tck/directives/coalesce.test.ts index 86ad4e871d..7198bd49a4 100644 --- a/packages/graphql/tests/tck/directives/coalesce.test.ts +++ b/packages/graphql/tests/tck/directives/coalesce.test.ts @@ -62,13 +62,13 @@ describe("Cypher coalesce()", () => { ) { users( where: { - id_EQ: $id - name_MATCHES: $name - NOT: { verified_EQ: $verified } - numberOfFriends_GT: $numberOfFriends - rating_LT: $rating - fromInterface_EQ: $fromInterface - toBeOverridden_EQ: $toBeOverridden + id: { eq: $id } + name: { matches: $name } + NOT: { verified: { eq: $verified } } + numberOfFriends: { gt: $numberOfFriends } + rating: { lt: $rating } + fromInterface: { eq: $fromInterface } + toBeOverridden: { eq: $toBeOverridden } } ) { name @@ -89,7 +89,8 @@ describe("Cypher coalesce()", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE (coalesce(this.id, \\"00000000-00000000-00000000-00000000\\") = $param0 AND coalesce(this.name, \\"Jane Smith\\") =~ $param1 AND coalesce(this.numberOfFriends, 0) > $param2 AND coalesce(this.rating, 2.5) < $param3 AND this.fromInterface = $param4 AND coalesce(this.toBeOverridden, \\"Overridden\\") = $param5 AND NOT (coalesce(this.verified, false) = $param6)) RETURN this { .name } AS this" `); @@ -135,7 +136,7 @@ describe("Cypher coalesce()", () => { const query = /* GraphQL */ ` query { - movies(where: { status_EQ: ACTIVE }) { + movies(where: { status: { eq: ACTIVE } }) { id status } @@ -145,7 +146,8 @@ describe("Cypher coalesce()", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE coalesce(this.status, \\"ACTIVE\\") = $param0 RETURN this { .id, .status } AS this" `); @@ -187,7 +189,7 @@ describe("Cypher coalesce()", () => { const query = /* GraphQL */ ` query Actors { actors { - moviesConnection(where: { node: { status_EQ: ACTIVE } }) { + moviesConnection(where: { node: { status: { eq: ACTIVE } } }) { edges { node { id @@ -202,7 +204,8 @@ describe("Cypher coalesce()", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -250,7 +253,7 @@ describe("Cypher coalesce()", () => { const query = /* GraphQL */ ` query Actors { actors { - moviesConnection(where: { node: { statuses_EQ: [ACTIVE, INACTIVE] } }) { + moviesConnection(where: { node: { statuses: { eq: [ACTIVE, INACTIVE] } } }) { edges { node { id @@ -265,7 +268,8 @@ describe("Cypher coalesce()", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) diff --git a/packages/graphql/tests/tck/directives/customResolver.test.ts b/packages/graphql/tests/tck/directives/customResolver.test.ts index 0354c0bdd2..fae3ba092c 100644 --- a/packages/graphql/tests/tck/directives/customResolver.test.ts +++ b/packages/graphql/tests/tck/directives/customResolver.test.ts @@ -58,7 +58,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .firstName } AS this" `); @@ -79,7 +80,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .firstName, .lastName } AS this" `); @@ -99,7 +101,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .firstName, .lastName } AS this" `); @@ -118,7 +121,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .firstName, .lastName } AS this" `); @@ -161,7 +165,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .firstName } AS this" `); @@ -180,7 +185,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { } AS this" `); @@ -188,168 +194,6 @@ describe("@customResolver directive", () => { }); }); - describe("Require fields on nested types", () => { - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type City @node { - name: String! - population: Int - } - - type Address @node { - street: String! - city: City! @relationship(type: "IN_CITY", direction: OUT) - } - - type User @node { - id: ID! - firstName: String! - lastName: String! - address: Address @relationship(type: "LIVES_AT", direction: OUT) - fullName: String - @customResolver(requires: "firstName lastName address { city { name population } }") - } - `; - - const resolvers = { - User: { - fullName: () => "The user's full name", - }, - }; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - resolvers, - }); - }); - - test("should not over fetch when all required fields are manually selected", async () => { - const query = /* GraphQL */ ` - { - users { - firstName - lastName - fullName - address { - city { - name - population - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:LIVES_AT]->(this1:Address) - CALL { - WITH this1 - MATCH (this1)-[this2:IN_CITY]->(this3:City) - WITH this3 { .name, .population } AS this3 - RETURN head(collect(this3)) AS var4 - } - WITH this1 { city: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .firstName, .lastName, address: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("should not fetch required fields if @customResolver field is not selected", async () => { - const query = /* GraphQL */ ` - { - users { - firstName - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - RETURN this { .firstName } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("should not over fetch when some required fields are manually selected", async () => { - const query = /* GraphQL */ ` - { - users { - lastName - fullName - address { - city { - population - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:LIVES_AT]->(this1:Address) - CALL { - WITH this1 - MATCH (this1)-[this2:IN_CITY]->(this3:City) - WITH this3 { .population, .name } AS this3 - RETURN head(collect(this3)) AS var4 - } - WITH this1 { city: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .lastName, .firstName, address: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("should not over fetch when no required fields are manually selected", async () => { - const query = /* GraphQL */ ` - { - users { - fullName - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:LIVES_AT]->(this1:Address) - CALL { - WITH this1 - MATCH (this1)-[this2:IN_CITY]->(this3:City) - WITH this3 { .name, .population } AS this3 - RETURN head(collect(this3)) AS var4 - } - WITH this1 { city: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .firstName, .lastName, address: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - }); - describe("Require fields on nested unions", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` @@ -366,12 +210,12 @@ describe("@customResolver directive", () => { type Book @node { title: String! - author: Author! @relationship(type: "WROTE", direction: IN) + author: [Author!]! @relationship(type: "WROTE", direction: IN) } type Journal @node { subject: String! - author: Author! @relationship(type: "WROTE", direction: IN) + author: [Author!]! @relationship(type: "WROTE", direction: IN) } `; @@ -408,7 +252,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -443,7 +288,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) RETURN this { .name } AS this" `); @@ -467,7 +313,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -502,7 +349,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -589,7 +437,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -624,7 +473,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) RETURN this { .name } AS this" `); @@ -648,7 +498,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -683,7 +534,8 @@ describe("@customResolver directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/cypher-interface.test.ts b/packages/graphql/tests/tck/directives/cypher/cypher-interface.test.ts index 381c6ba73a..87c31dea3e 100644 --- a/packages/graphql/tests/tck/directives/cypher/cypher-interface.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/cypher-interface.test.ts @@ -96,7 +96,6 @@ describe("Cypher directive on interface", () => { id: ID title: String actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - topActor: Actor @relationship(type: "ACTED_IN", direction: IN) } type Query { @@ -140,7 +139,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -185,7 +185,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -218,110 +219,13 @@ describe("Cypher directive on interface", () => { `); }); - test("top-level interface with nested projection", async () => { - const query = /* GraphQL */ ` - query { - moviesOrTVShows(title: "The Matrix") { - title - ... on Movie { - actors { - name - } - topActor { - name - } - } - ... on TVShow { - actors { - name - } - topActor { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (n) - WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) - RETURN n - } - WITH n AS this0 - CALL { - WITH this0 - CALL { - WITH * - MATCH (this0) - WHERE this0:TVShow - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this1 - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this3 - WITH this3 { .name } AS this3 - RETURN head(collect(this3)) AS var4 - } - WITH this0 { .title, actors: var2, topActor: var4, __resolveType: \\"TVShow\\", __id: id(this0) } AS this0 - RETURN this0 AS var5 - UNION - WITH * - MATCH (this0) - WHERE this0:Movie - CALL { - WITH this0 - MATCH (this0)<-[this6:ACTED_IN]-(this7:Actor) - WITH this7 { .name } AS this7 - RETURN collect(this7) AS var8 - } - CALL { - WITH this0 - MATCH (this0)<-[this9:ACTED_IN]-(this10:Actor) - WITH this10 { .name } AS this10 - RETURN head(collect(this10)) AS var11 - } - WITH this0 { .title, actors: var8, topActor: var11, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 - RETURN this0 AS var5 - } - RETURN var5 - } - RETURN var5 AS this0" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"The Matrix\\" - }" - `); - }); - test("top-level interface with nested relationship parameters", async () => { const query = /* GraphQL */ ` query { moviesOrTVShows(title: "The Matrix") { title ... on Movie { - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -340,7 +244,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -386,6 +291,7 @@ describe("Cypher directive on interface", () => { WITH this0 MATCH (this0)<-[this6:ACTED_IN]-(this7:Actor) WHERE this7.name = $param2 + WITH DISTINCT this7 WITH this7 { .name } AS this7 RETURN collect(this7) AS var8 } @@ -418,7 +324,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -464,7 +371,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -498,111 +406,13 @@ describe("Cypher directive on interface", () => { `); }); - test("top-level single interface with nested projection", async () => { - const query = /* GraphQL */ ` - query { - movieOrTVShow(title: "The Matrix") { - title - ... on Movie { - actors { - name - } - topActor { - name - } - } - ... on TVShow { - actors { - name - } - topActor { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (n) - WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) - RETURN n - LIMIT 1 - } - WITH n AS this0 - CALL { - WITH this0 - CALL { - WITH * - MATCH (this0) - WHERE this0:TVShow - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this1 - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this3 - WITH this3 { .name } AS this3 - RETURN head(collect(this3)) AS var4 - } - WITH this0 { .title, actors: var2, topActor: var4, __resolveType: \\"TVShow\\", __id: id(this0) } AS this0 - RETURN this0 AS var5 - UNION - WITH * - MATCH (this0) - WHERE this0:Movie - CALL { - WITH this0 - MATCH (this0)<-[this6:ACTED_IN]-(this7:Actor) - WITH this7 { .name } AS this7 - RETURN collect(this7) AS var8 - } - CALL { - WITH this0 - MATCH (this0)<-[this9:ACTED_IN]-(this10:Actor) - WITH this10 { .name } AS this10 - RETURN head(collect(this10)) AS var11 - } - WITH this0 { .title, actors: var8, topActor: var11, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 - RETURN this0 AS var5 - } - RETURN var5 - } - RETURN var5 AS this0" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"The Matrix\\" - }" - `); - }); - test("top-level single interface with nested relationship parameters", async () => { const query = /* GraphQL */ ` query { moviesOrTVShows(title: "The Matrix") { title ... on Movie { - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -621,7 +431,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -667,6 +478,7 @@ describe("Cypher directive on interface", () => { WITH this0 MATCH (this0)<-[this6:ACTED_IN]-(this7:Actor) WHERE this7.name = $param2 + WITH DISTINCT this7 WITH this7 { .name } AS this7 RETURN collect(this7) AS var8 } @@ -694,7 +506,7 @@ describe("Cypher directive on interface", () => { moviesOrTVShows(title: "The Matrix") { title ... on Movie { - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -714,7 +526,8 @@ describe("Cypher directive on interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -765,6 +578,7 @@ describe("Cypher directive on interface", () => { WITH this0 MATCH (this0)<-[this6:ACTED_IN]-(this7:Actor) WHERE this7.name = $param2 + WITH DISTINCT this7 WITH this7 { .name } AS this7 RETURN collect(this7) AS var8 } diff --git a/packages/graphql/tests/tck/directives/cypher/cypher-union.test.ts b/packages/graphql/tests/tck/directives/cypher/cypher-union.test.ts index 1468e5620f..8c45991781 100644 --- a/packages/graphql/tests/tck/directives/cypher/cypher-union.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/cypher-union.test.ts @@ -94,7 +94,6 @@ describe("Cypher directive on union", () => { id: ID title: String actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - topActor: Actor @relationship(type: "ACTED_IN", direction: IN) } type Query { @@ -143,7 +142,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -188,7 +188,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -221,111 +222,13 @@ describe("Cypher directive on union", () => { `); }); - test("top-level union with nested projection", async () => { - const query = /* GraphQL */ ` - query { - moviesOrTVShows(title: "The Matrix") { - ... on Movie { - title - actors { - name - } - topActor { - name - } - } - ... on TVShow { - title - actors { - name - } - topActor { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (n) - WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) - RETURN n - } - WITH n AS this0 - CALL { - WITH this0 - CALL { - WITH * - MATCH (this0) - WHERE this0:Movie - CALL { - WITH this0 - MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - CALL { - WITH this0 - MATCH (this0)<-[this4:ACTED_IN]-(this5:Actor) - WITH this5 { .name } AS this5 - RETURN head(collect(this5)) AS var6 - } - WITH this0 { .title, actors: var3, topActor: var6, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 - RETURN this0 AS var7 - UNION - WITH * - MATCH (this0) - WHERE this0:TVShow - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this8 - WITH this8 { .name } AS this8 - RETURN collect(this8) AS var9 - } - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this10 - WITH this10 { .name } AS this10 - RETURN head(collect(this10)) AS var11 - } - WITH this0 { .title, actors: var9, topActor: var11, __resolveType: \\"TVShow\\", __id: id(this0) } AS this0 - RETURN this0 AS var7 - } - RETURN var7 - } - RETURN var7 AS this0" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"The Matrix\\" - }" - `); - }); - test("top-level union with nested relationship parameters", async () => { const query = /* GraphQL */ ` query { moviesOrTVShows(title: "The Matrix") { ... on Movie { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -345,7 +248,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -361,6 +265,7 @@ describe("Cypher directive on union", () => { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) WHERE this2.name = $param1 + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -428,7 +333,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -474,7 +380,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -508,112 +415,13 @@ describe("Cypher directive on union", () => { `); }); - test("top-level single union with nested projection", async () => { - const query = /* GraphQL */ ` - query { - movieOrTVShow(title: "The Matrix") { - ... on Movie { - title - actors { - name - } - topActor { - name - } - } - ... on TVShow { - title - actors { - name - } - topActor { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - MATCH (n) - WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) - RETURN n - LIMIT 1 - } - WITH n AS this0 - CALL { - WITH this0 - CALL { - WITH * - MATCH (this0) - WHERE this0:Movie - CALL { - WITH this0 - MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - CALL { - WITH this0 - MATCH (this0)<-[this4:ACTED_IN]-(this5:Actor) - WITH this5 { .name } AS this5 - RETURN head(collect(this5)) AS var6 - } - WITH this0 { .title, actors: var3, topActor: var6, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 - RETURN this0 AS var7 - UNION - WITH * - MATCH (this0) - WHERE this0:TVShow - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this8 - WITH this8 { .name } AS this8 - RETURN collect(this8) AS var9 - } - CALL { - WITH this0 - CALL { - WITH this0 - WITH this0 AS this - MATCH (a:Actor) - RETURN a - } - WITH a AS this10 - WITH this10 { .name } AS this10 - RETURN head(collect(this10)) AS var11 - } - WITH this0 { .title, actors: var9, topActor: var11, __resolveType: \\"TVShow\\", __id: id(this0) } AS this0 - RETURN this0 AS var7 - } - RETURN var7 - } - RETURN var7 AS this0" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"The Matrix\\" - }" - `); - }); - test("top-level single union with nested relationship parameters", async () => { const query = /* GraphQL */ ` query { moviesOrTVShows(title: "The Matrix") { ... on Movie { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -633,7 +441,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (n) WHERE (n:TVShow OR n:Movie) AND ($param0 IS NULL OR n.title = $param0) RETURN n @@ -649,6 +458,7 @@ describe("Cypher directive on union", () => { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) WHERE this2.name = $param1 + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -706,7 +516,7 @@ describe("Cypher directive on union", () => { moviesOrTVShows(title: "The Matrix") { ... on Movie { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -727,7 +537,8 @@ describe("Cypher directive on union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -748,6 +559,7 @@ describe("Cypher directive on union", () => { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) WHERE this2.name = $param1 + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } diff --git a/packages/graphql/tests/tck/directives/cypher/cypher.test.ts b/packages/graphql/tests/tck/directives/cypher/cypher.test.ts index 88445a264b..950ea3ae26 100644 --- a/packages/graphql/tests/tck/directives/cypher/cypher.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/cypher.test.ts @@ -113,7 +113,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -144,7 +145,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -173,7 +175,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH * LIMIT $param0 CALL { @@ -211,7 +214,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -256,7 +260,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -315,7 +320,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -393,7 +399,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -469,7 +476,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (m:Movie {title: $param0}) RETURN m } @@ -477,6 +485,7 @@ describe("Cypher directive", () => { CALL { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -531,7 +540,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (m:Movie {title: $param0}) RETURN m } @@ -539,6 +549,7 @@ describe("Cypher directive", () => { CALL { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -592,7 +603,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -605,6 +617,7 @@ describe("Cypher directive", () => { CALL { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-aggregation.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-aggregation.test.ts index 380d345986..6df76c489a 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-aggregation.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-aggregation.test.ts @@ -22,23 +22,24 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Aggregation", () => { test("String aggregation", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String released: Int - custom_field: String @cypher( - statement: """ - MATCH (this) - RETURN this.custom_field as s - """ - columnName: "s" - ) + custom_field: String + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field as s + """ + columnName: "s" + ) } `; - const query = ` + const query = /* GraphQL */ ` query { - moviesAggregate(where: { custom_field_STARTS_WITH: "he" }) { + moviesAggregate(where: { custom_field: { startsWith: "he" } }) { title { shortest } @@ -53,7 +54,8 @@ describe("cypher directive filtering - Aggregation", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) CALL { WITH this @@ -84,23 +86,24 @@ describe("cypher directive filtering - Aggregation", () => { }); test("Int aggregation", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String released: Int - custom_field: Int @cypher( - statement: """ - MATCH (this) - RETURN this.custom_field as s - """ - columnName: "s" - ) + custom_field: Int + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field as s + """ + columnName: "s" + ) } `; - const query = ` + const query = /* GraphQL */ ` query { - moviesAggregate(where: { custom_field_GT: 0 }) { + moviesAggregate(where: { custom_field: { gt: 0 } }) { released { min } @@ -115,7 +118,8 @@ describe("cypher directive filtering - Aggregation", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) CALL { WITH this @@ -146,23 +150,24 @@ describe("cypher directive filtering - Aggregation", () => { }); test("String list aggregation", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String released: Int - custom_field: [String] @cypher( - statement: """ - MATCH (this) - RETURN this.custom_field as s - """ - columnName: "s" - ) + custom_field: [String] + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field as s + """ + columnName: "s" + ) } `; - const query = ` + const query = /* GraphQL */ ` query { - moviesAggregate(where: { custom_field_INCLUDES: "test" }) { + moviesAggregate(where: { custom_field: { includes: "test" } }) { title { longest } @@ -177,7 +182,8 @@ describe("cypher directive filtering - Aggregation", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) CALL { WITH this @@ -209,23 +215,24 @@ describe("cypher directive filtering - Aggregation", () => { }); test("Int list aggregation", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String released: Int - custom_field: [Int] @cypher( - statement: """ - MATCH (this) - RETURN this.custom_field as s - """ - columnName: "s" - ) + custom_field: [Int] + @cypher( + statement: """ + MATCH (this) + RETURN this.custom_field as s + """ + columnName: "s" + ) } `; - const query = ` + const query = /* GraphQL */ ` query { - moviesAggregate(where: { custom_field_INCLUDES: 2 }) { + moviesAggregate(where: { custom_field: { includes: 2 } }) { title { longest } @@ -240,7 +247,8 @@ describe("cypher directive filtering - Aggregation", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Movie) CALL { WITH this diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-auth.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-auth.test.ts index db1e481dbc..537ddda2ff 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-auth.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-auth.test.ts @@ -23,8 +23,10 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Auth", () => { test("With authorization on type using @cypher return value", async () => { - const typeDefs = ` - type Movie @node @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) { title: String custom_field: String @cypher( @@ -36,7 +38,7 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -44,7 +46,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { movies { title @@ -64,7 +66,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -92,7 +95,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With authorization on @cypher field using @cypher return value", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -102,11 +105,11 @@ describe("cypher directive filtering - Auth", () => { """ columnName: "s" ) - @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -114,7 +117,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { movies { custom_field @@ -134,7 +137,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -172,7 +176,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With authorization on @cypher field using different field return value", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -182,11 +186,11 @@ describe("cypher directive filtering - Auth", () => { """ columnName: "s" ) - @authorization(filter: [{ where: { node: { title: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { title: { eq: "$jwt.custom_value" } } } }]) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -194,7 +198,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { movies { title @@ -214,7 +218,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) RETURN this { .title } AS this" `); @@ -222,7 +227,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With authorization on Actor type field using nested Movie's @cypher field return value", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -235,7 +240,11 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor @authorization(filter: [{ where: { node: { movies_SOME: { custom_field: "$jwt.custom_value" } } } }]) { + type Actor + @node + @authorization( + filter: [{ where: { node: { movies: { some: { custom_field: { eq: "$jwt.custom_value" } } } } } }] + ) { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -243,7 +252,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { actors { name @@ -263,7 +272,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -298,9 +308,10 @@ describe("cypher directive filtering - Auth", () => { }); test("With authorization on a different field than the @cypher field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { - title: String @authorization(filter: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) + title: String + @authorization(filter: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) custom_field: String @cypher( statement: """ @@ -311,7 +322,7 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -319,7 +330,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { movies { title @@ -339,7 +350,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -368,7 +380,9 @@ describe("cypher directive filtering - Auth", () => { test("With authorization on type using @cypher return value, with validate", async () => { const typeDefs = /* GraphQL */ ` - type Movie @node @authorization(validate: [{ where: { node: { custom_field: "$jwt.custom_value" } } }]) { + type Movie + @node + @authorization(validate: [{ where: { node: { custom_field: { eq: "$jwt.custom_value" } } } }]) { title: String custom_field: String @cypher( @@ -389,7 +403,7 @@ describe("cypher directive filtering - Auth", () => { const token = createBearerToken("secret", { custom_value: "hello" }); - const query = ` + const query = /* GraphQL */ ` query { movies { title @@ -409,7 +423,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-connect.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-connect.test.ts index 3a99befb56..d60a5d3ff5 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-connect.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-connect.test.ts @@ -22,13 +22,13 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering", () => { test("Connect filter", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String custom_field: String @cypher( @@ -41,7 +41,7 @@ describe("cypher directive filtering", () => { } `; - const query = ` + const query = /* GraphQL */ ` mutation { createMovies( input: [ @@ -51,20 +51,11 @@ describe("cypher directive filtering", () => { connect: [ { where: { - node: { - name: "Keanu Reeves", - custom_field: "hello world!" - } - } - } - ] - create: [ - { - node: { - name: "Jada Pinkett Smith" + node: { name: { eq: "Keanu Reeves" }, custom_field: { eq: "hello world!" } } } } ] + create: [{ node: { name: "Jada Pinkett Smith" } }] } } ] @@ -86,7 +77,8 @@ describe("cypher directive filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -119,7 +111,7 @@ describe("cypher directive filtering", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actors_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_actors_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_actors_connect0_node) } } WITH this0, this0_actors_connect0_node @@ -132,6 +124,7 @@ describe("cypher directive filtering", () => { CALL { WITH this0 MATCH (this0)<-[create_this0:ACTED_IN]-(create_this1:Actor) + WITH DISTINCT create_this1 WITH create_this1 { .name } AS create_this1 RETURN collect(create_this1) AS create_var2 } diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list-auth.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list-auth.test.ts index 2739cc795b..43c5a9c309 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list-auth.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list-auth.test.ts @@ -26,7 +26,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { custom_list_INCLUDES: "$jwt.custom_value" } } }]) { + @authorization(filter: [{ where: { node: { custom_list: { includes: "$jwt.custom_value" } } } }]) { title: String custom_list: [String] @cypher( @@ -39,7 +39,7 @@ describe("cypher directive filtering - List Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -67,7 +67,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -112,7 +113,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { custom_list_INCLUDES: "$jwt.custom_value" } } }]) { + @authorization(filter: [{ where: { node: { custom_list: { includes: "$jwt.custom_value" } } } }]) { title: String custom_list: [String] @cypher( @@ -125,7 +126,7 @@ describe("cypher directive filtering - List Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -153,7 +154,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -194,11 +196,11 @@ describe("cypher directive filtering - List Auth", () => { """ columnName: "list" ) - @authorization(filter: [{ where: { node: { custom_list_INCLUDES: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_list: { includes: "$jwt.custom_value" } } } }]) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -226,7 +228,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -279,11 +282,11 @@ describe("cypher directive filtering - List Auth", () => { """ columnName: "list" ) - @authorization(filter: [{ where: { node: { custom_list: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_list: { eq: "$jwt.custom_value" } } } }]) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -311,7 +314,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) RETURN this { .title } AS this" `); @@ -334,7 +338,10 @@ describe("cypher directive filtering - List Auth", () => { } type Actor - @authorization(filter: [{ where: { node: { movies_SOME: { custom_list: "$jwt.custom_value" } } } }]) { + @node + @authorization( + filter: [{ where: { node: { movies: { some: { custom_list: { eq: "$jwt.custom_value" } } } } } }] + ) { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -362,7 +369,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -402,7 +410,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type Movie @node { title: String - @authorization(filter: [{ where: { node: { custom_list_INCLUDES: "$jwt.custom_value" } } }]) + @authorization(filter: [{ where: { node: { custom_list: { includes: "$jwt.custom_value" } } } }]) custom_list: [String] @cypher( statement: """ @@ -414,7 +422,7 @@ describe("cypher directive filtering - List Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -442,7 +450,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -475,7 +484,7 @@ describe("cypher directive filtering - List Auth", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(validate: [{ where: { node: { custom_list_INCLUDES: "$jwt.custom_value" } } }]) { + @authorization(validate: [{ where: { node: { custom_list: { includes: "$jwt.custom_value" } } } }]) { title: String custom_list: [String] @cypher( @@ -516,7 +525,8 @@ describe("cypher directive filtering - List Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list.test.ts index fead0e413b..df6c60f92a 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-list.test.ts @@ -22,10 +22,10 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Lists", () => { test("Int cypher field AND String title field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String - custom_cypher_list: [String] + custom_cypher_list: [String] @cypher( statement: """ RETURN ['a', 'b', 'c'] as list @@ -35,9 +35,9 @@ describe("cypher directive filtering - Lists", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { custom_cypher_list_INCLUDES: "a" }) { + movies(where: { custom_cypher_list: { includes: "a" } }) { title } } @@ -50,7 +50,8 @@ describe("cypher directive filtering - Lists", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-misc.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-misc.test.ts index d724f13b39..009a5aba99 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-misc.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-misc.test.ts @@ -22,7 +22,7 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Auth", () => { test("With relationship filter (non-Cypher field)", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -35,21 +35,16 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } `; - const query = ` + const query = /* GraphQL */ ` query { movies( - where: { - custom_field: "hello world!" - actors_SOME: { - name: "Keanu Reeves" - } - } + where: { custom_field: { eq: "hello world!" }, actors: { some: { name: { eq: "Keanu Reeves" } } } } ) { custom_field title @@ -67,7 +62,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -96,6 +92,7 @@ describe("cypher directive filtering - Auth", () => { CALL { WITH this MATCH (this)<-[this5:ACTED_IN]-(this6:Actor) + WITH DISTINCT this6 WITH this6 { .name } AS this6 RETURN collect(this6) AS var7 } @@ -111,7 +108,7 @@ describe("cypher directive filtering - Auth", () => { }); test("In a nested filter", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -124,17 +121,17 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } `; - const query = ` + const query = /* GraphQL */ ` query { actors { name - movies(where: { custom_field: "hello world!"}) { + movies(where: { custom_field: { eq: "hello world!" } }) { title } } @@ -148,10 +145,12 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) + WITH DISTINCT this1 CALL { WITH this1 CALL { @@ -178,7 +177,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With a nested filter", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -191,17 +190,17 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { custom_field: "hello world!" }) { + movies(where: { custom_field: { eq: "hello world!" } }) { title - actors(where: { name: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -215,7 +214,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -232,6 +232,7 @@ describe("cypher directive filtering - Auth", () => { WITH this MATCH (this)<-[this2:ACTED_IN]-(this3:Actor) WHERE this3.name = $param1 + WITH DISTINCT this3 WITH this3 { .name } AS this3 RETURN collect(this3) AS var4 } @@ -247,7 +248,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With two cypher fields", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -267,7 +268,7 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String another_custom_field: String @cypher( @@ -280,9 +281,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { custom_field: "hello world!", another_custom_field_GT: 50 }) { + movies(where: { custom_field: { eq: "hello world!" }, another_custom_field: { gt: 50 } }) { title actors { name @@ -298,7 +299,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -324,6 +326,7 @@ describe("cypher directive filtering - Auth", () => { CALL { WITH this MATCH (this)<-[this4:ACTED_IN]-(this5:Actor) + WITH DISTINCT this5 WITH this5 { .name } AS this5 RETURN collect(this5) AS var6 } @@ -342,7 +345,7 @@ describe("cypher directive filtering - Auth", () => { }); test("With two cypher fields, one nested", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -355,7 +358,7 @@ describe("cypher directive filtering - Auth", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String another_custom_field: String @cypher( @@ -368,11 +371,11 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { custom_field: "hello world!" }) { + movies(where: { custom_field: { eq: "hello world!" } }) { title - actors(where: { another_custom_field: "goodbye!" name: "Keanu Reeves" }) { + actors(where: { another_custom_field: { eq: "goodbye!" }, name: { eq: "Keanu Reeves" } }) { name } } @@ -386,7 +389,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -402,6 +406,7 @@ describe("cypher directive filtering - Auth", () => { CALL { WITH this MATCH (this)<-[this2:ACTED_IN]-(this3:Actor) + WITH DISTINCT this3 CALL { WITH this3 CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.test.ts index d912dac7ad..3d5a7158f7 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-one-to-one-relationship.test.ts @@ -52,7 +52,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { actor: { name_EQ: "Keanu Reeves" } }) { + movies(where: { actor: { name: { eq: "Keanu Reeves" } } }) { title } } @@ -65,7 +65,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -120,7 +121,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { released: 2003, actor: { name_EQ: "Keanu Reeves", age_GT: 30 } }) { + movies(where: { released: { eq: 2003 }, actor: { name: { eq: "Keanu Reeves" }, age: { gt: 30 } } }) { title } } @@ -133,7 +134,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -198,7 +200,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { actor: { name_EQ: "Keanu Reeves" } }) { + movies(where: { actor: { name: { eq: "Keanu Reeves" } } }) { title actor { name @@ -210,7 +212,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -245,7 +248,7 @@ describe("cypher directive filtering - One To One Relationship", () => { }" `); }); - + // TODO: {actor: null} was not migrated to {actors: {eq: null}}. Check if this is correct test("1 to 1 relationship with null filter", async () => { const typeDefs = /* GraphQL */ ` type Movie @node { @@ -280,7 +283,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { released_EQ: 2003, actor: null }) { + movies(where: { released: { eq: 2003 }, actor: null }) { title } } @@ -289,7 +292,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -315,7 +319,7 @@ describe("cypher directive filtering - One To One Relationship", () => { }" `); }); - + // TODO: {actor: null} was not migrated to {actors: {eq: null}}. Check if this is correct test("1 to 1 relationship with NOT null filter", async () => { const typeDefs = /* GraphQL */ ` type Movie @node { @@ -350,7 +354,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { AND: [{ released_IN: [2003], NOT: { actor: null } }] }) { + movies(where: { AND: [{ released: { in: [2003] }, NOT: { actor: null } }] }) { title } } @@ -359,7 +363,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -392,7 +397,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization(filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) { title: String released: Int directed_by: Person! @@ -431,7 +436,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -445,7 +450,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -515,7 +521,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization(filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }]) { title: String released: Int directed_by: Person! @@ -554,7 +560,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -568,7 +574,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -640,7 +647,9 @@ describe("cypher directive filtering - One To One Relationship", () => { title: String released: Int directed_by: Person! - @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @authorization( + filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:Person) @@ -676,7 +685,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -690,7 +699,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -762,7 +772,9 @@ describe("cypher directive filtering - One To One Relationship", () => { title: String released: Int directed_by: Person! - @authorization(filter: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @authorization( + filter: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:Person) @@ -798,7 +810,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -812,7 +824,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -882,7 +895,9 @@ describe("cypher directive filtering - One To One Relationship", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) { title: String released: Int directed_by: Person! @@ -921,7 +936,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -935,7 +950,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -1005,7 +1021,9 @@ describe("cypher directive filtering - One To One Relationship", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) { title: String released: Int directed_by: Person! @@ -1044,7 +1062,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1058,7 +1076,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -1130,7 +1149,9 @@ describe("cypher directive filtering - One To One Relationship", () => { title: String released: Int directed_by: Person! - @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @authorization( + validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:Person) @@ -1166,7 +1187,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1180,7 +1201,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -1252,7 +1274,9 @@ describe("cypher directive filtering - One To One Relationship", () => { title: String released: Int directed_by: Person! - @authorization(validate: [{ where: { node: { directed_by: { name_EQ: "$jwt.custom_value" } } } }]) + @authorization( + validate: [{ where: { node: { directed_by: { name: { eq: "$jwt.custom_value" } } } } }] + ) @cypher( statement: """ MATCH (this)<-[:DIRECTED]-(director:Person) @@ -1288,7 +1312,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1302,7 +1326,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -1404,7 +1429,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - people(where: { directed: { title_EQ: "The Matrix" } }) { + people(where: { directed: { title: { eq: "The Matrix" } } }) { directed { title directed_by { @@ -1427,7 +1452,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this CALL { @@ -1453,9 +1479,11 @@ describe("cypher directive filtering - One To One Relationship", () => { CALL { WITH this2 MATCH (this2)<-[this3:ACTED_IN]-(this4:Person) + WITH DISTINCT this4 CALL { WITH this4 MATCH (this4)-[this5:ACTED_IN]->(this6:Movie) + WITH DISTINCT this6 CALL { WITH this6 CALL { @@ -1535,7 +1563,7 @@ describe("cypher directive filtering - One To One Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { directed_by: { name_EQ: "Lilly Wachowski" }, title_ENDS_WITH: "Matrix" }) { + movies(where: { directed_by: { name: { eq: "Lilly Wachowski" } }, title: { endsWith: "Matrix" } }) { actorsConnection { totalCount edges { @@ -1551,7 +1579,8 @@ describe("cypher directive filtering - One To One Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-scalar.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-scalar.test.ts index bc75c398b1..7a399b05f5 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-scalar.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-scalar.test.ts @@ -20,9 +20,9 @@ import { Neo4jGraphQL } from "../../../../../src"; import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-test-utils"; -describe("cypher directive filtering - Auth", () => { +describe("cypher directive filtering", () => { test("Int cypher field AND String title field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_count: Int @@ -36,9 +36,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { special_count_GTE: 1, title: "CustomType One" }) { + movies(where: { special_count: { gte: 1 }, title: { eq: "CustomType One" } }) { special_count } } @@ -51,7 +51,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -91,7 +92,7 @@ describe("cypher directive filtering - Auth", () => { }); test("unmatched Int cypher field AND String title field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_count: Int @@ -105,9 +106,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { special_count_GTE: 1, title: "CustomType Unknown" }) { + movies(where: { special_count: { gte: 1 }, title: { eq: "CustomType Unknown" } }) { special_count } } @@ -120,7 +121,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -160,7 +162,7 @@ describe("cypher directive filtering - Auth", () => { }); test("Int cypher field, selecting String title field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_count: Int @@ -174,9 +176,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies(where: { special_count_GTE: 1 }) { + movies(where: { special_count: { gte: 1 } }) { title } } @@ -189,7 +191,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-sorting.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-sorting.test.ts index 2cb39091c2..c1acbcb76f 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-sorting.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-sorting.test.ts @@ -22,7 +22,7 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering", () => { test("With sorting on the return value", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -36,7 +36,7 @@ describe("cypher directive filtering", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -44,7 +44,7 @@ describe("cypher directive filtering", () => { const query = /* GraphQL */ ` query { - movies(where: { custom_field_STARTS_WITH: "The Matrix" }, sort: [{ custom_field: DESC }]) { + movies(where: { custom_field: { startsWith: "The Matrix" } }, sort: [{ custom_field: DESC }]) { title actors { name @@ -60,7 +60,8 @@ describe("cypher directive filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -90,6 +91,7 @@ describe("cypher directive filtering", () => { CALL { WITH this MATCH (this)<-[this4:ACTED_IN]-(this5:Actor) + WITH DISTINCT this5 WITH this5 { .name } AS this5 RETURN collect(this5) AS var6 } @@ -104,7 +106,7 @@ describe("cypher directive filtering", () => { }); test("With sorting on the return value of a different field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String custom_field: String @@ -117,7 +119,7 @@ describe("cypher directive filtering", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - type Actor { + type Actor @node { name: String movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } @@ -125,7 +127,7 @@ describe("cypher directive filtering", () => { const query = /* GraphQL */ ` query { - movies(where: { custom_field: "hello world!" }, sort: [{ title: DESC }]) { + movies(where: { custom_field: { eq: "hello world!" } }, sort: [{ title: DESC }]) { title actors { name @@ -141,7 +143,8 @@ describe("cypher directive filtering", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -159,6 +162,7 @@ describe("cypher directive filtering", () => { CALL { WITH this MATCH (this)<-[this2:ACTED_IN]-(this3:Actor) + WITH DISTINCT this3 WITH this3 { .name } AS this3 RETURN collect(this3) AS var4 } diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-spatial.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-spatial.test.ts index 26b9e38b95..6090e13245 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-spatial.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-spatial.test.ts @@ -22,7 +22,7 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Auth", () => { test("Point cypher field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_location: Point @@ -35,16 +35,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies( - where: { - special_location_DISTANCE: { - point: { latitude: 1, longitude: 1 } - distance: 0 - } - } - ) { + movies(where: { special_location_DISTANCE: { point: { latitude: 1, longitude: 1 }, distance: 0 } }) { title special_location { latitude @@ -61,7 +54,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -101,7 +95,7 @@ describe("cypher directive filtering - Auth", () => { }); test("CartesianPoint cypher field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_location: CartesianPoint @@ -114,16 +108,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies( - where: { - special_location_DISTANCE: { - point: { x: 1, y: 1, z: 2 } - distance: 1 - } - } - ) { + movies(where: { special_location_DISTANCE: { point: { x: 1, y: 1, z: 2 }, distance: 1 } }) { title special_location { x @@ -141,7 +128,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts index a1acd09c56..12e93dd912 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts @@ -22,7 +22,7 @@ import { formatCypher, formatParams, translateQuery } from "../../../utils/tck-t describe("cypher directive filtering - Auth", () => { test("DateTime cypher field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_time: DateTime @@ -35,13 +35,9 @@ describe("cypher directive filtering - Auth", () => { } `; - const query = ` + const query = /* GraphQL */ ` query { - movies( - where: { - special_time_GT: "2024-09-02T00:00:00Z" - } - ) { + movies(where: { special_time: { gt: "2024-09-02T00:00:00Z" } }) { special_time title } @@ -55,7 +51,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -67,7 +64,7 @@ describe("cypher directive filtering - Auth", () => { RETURN this0 AS var1 } WITH * - WHERE var1 > $param0 + WHERE var1 > datetime($param0) CALL { WITH this CALL { @@ -83,22 +80,13 @@ describe("cypher directive filtering - Auth", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2024, - \\"month\\": 9, - \\"day\\": 2, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2024-09-02T00:00:00Z\\" }" `); }); test("Duration cypher field", async () => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Movie @node { title: String special_duration: Duration @@ -110,13 +98,9 @@ describe("cypher directive filtering - Auth", () => { ) } `; - const query = ` + const query = /* GraphQL */ ` query { - movies( - where: { - special_duration: "P14DT16H12M" - } - ) { + movies(where: { special_duration: { eq: "P14DT16H12M" } }) { title } } @@ -129,7 +113,8 @@ describe("cypher directive filtering - Auth", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.test.ts index af15457e57..8e9c08e087 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-auth.test.ts @@ -26,7 +26,9 @@ describe("cypher directive filtering - relationship auth filter", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + filter: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }] + ) { title: String rating: Float actors: [Actor!]! @@ -65,7 +67,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { rating_LT: 7.0 }) { + moviesConnection(where: { rating: { lt: 7.0 } }) { edges { node { title @@ -78,7 +80,8 @@ describe("cypher directive filtering - relationship auth filter", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -118,7 +121,9 @@ describe("cypher directive filtering - relationship auth filter", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(filter: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + filter: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }] + ) { title: String rating: Float actors: [Actor!]! @@ -157,7 +162,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { rating_LT: 7.0 }) { + moviesConnection(where: { rating: { lt: 7.0 } }) { edges { node { title @@ -170,7 +175,8 @@ describe("cypher directive filtering - relationship auth filter", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -210,7 +216,9 @@ describe("cypher directive filtering - relationship auth filter", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + validate: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }] + ) { title: String rating: Float actors: [Actor!]! @@ -249,7 +257,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { rating_LT: 7.0 }) { + moviesConnection(where: { rating: { lt: 7.0 } }) { edges { node { title @@ -262,7 +270,8 @@ describe("cypher directive filtering - relationship auth filter", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -302,7 +311,9 @@ describe("cypher directive filtering - relationship auth filter", () => { const typeDefs = /* GraphQL */ ` type Movie @node - @authorization(validate: [{ where: { node: { actors_SOME: { name_EQ: "$jwt.custom_value" } } } }]) { + @authorization( + validate: [{ where: { node: { actors: { some: { name: { eq: "$jwt.custom_value" } } } } } }] + ) { title: String rating: Float actors: [Actor!]! @@ -341,7 +352,7 @@ describe("cypher directive filtering - relationship auth filter", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { rating_GT: 7.0 }) { + moviesConnection(where: { rating: { gt: 7.0 } }) { edges { node { title @@ -354,7 +365,8 @@ describe("cypher directive filtering - relationship auth filter", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.test.ts index 4792405991..dcc2eb717f 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship-connection.test.ts @@ -54,7 +54,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { NOT: { actors_SOME: { name_EQ: "Jada Pinkett Smith" } } }) { + moviesConnection(where: { NOT: { actors: { some: { name: { eq: "Jada Pinkett Smith" } } } } }) { edges { node { title @@ -67,7 +67,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -131,7 +132,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { actors_ALL: { name_EQ: "Keanu Reeves" } }) { + moviesConnection(where: { actors: { all: { name: { eq: "Keanu Reeves" } } } }) { edges { node { title @@ -144,7 +145,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -208,7 +210,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { actors_SINGLE: { name_EQ: "Carrie-Anne Moss" } }) { + moviesConnection(where: { actors: { single: { name: { eq: "Carrie-Anne Moss" } } } }) { edges { node { title @@ -221,7 +223,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -285,7 +288,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { actors_SOME: { name_EQ: "Keanu Reeves" } }) { + moviesConnection(where: { actors: { some: { name: { eq: "Keanu Reeves" } } } }) { edges { node { title @@ -298,7 +301,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -362,7 +366,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { actors_SOME: { name_EQ: "Keanu Reeves" } }, sort: { title: DESC }) { + moviesConnection(where: { actors: { some: { name: { eq: "Keanu Reeves" } } } }, sort: { title: DESC }) { edges { node { title @@ -375,7 +379,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -441,7 +446,7 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - moviesConnection(where: { actors_NONE: { name_EQ: "Keanu Reeves" } }) { + moviesConnection(where: { actors: { none: { name: { eq: "Keanu Reeves" } } } }) { edges { node { title @@ -454,7 +459,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { @@ -541,8 +547,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { moviesConnection( where: { OR: [ - { actors_SOME: { name_EQ: "Jada Pinkett Smith" } } - { genres_SOME: { name_EQ: "Romance" } } + { actors: { some: { name: { eq: "Jada Pinkett Smith" } } } } + { genres: { some: { name: { eq: "Romance" } } } } ] } ) { @@ -558,7 +564,8 @@ describe("Connection API - cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) CALL { WITH this0 CALL { diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship.test.ts index 5dfc575479..143184bbf8 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/relationships/cypher-filtering-relationship.test.ts @@ -54,7 +54,7 @@ describe("cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { NOT: { actors_SOME: { name_EQ: "Jada Pinkett Smith" } } }) { + movies(where: { NOT: { actors: { some: { name: { eq: "Jada Pinkett Smith" } } } } }) { title } } @@ -63,7 +63,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -119,7 +120,7 @@ describe("cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { actors_ALL: { name_EQ: "Keanu Reeves" } }) { + movies(where: { actors: { all: { name: { eq: "Keanu Reeves" } } } }) { title } } @@ -128,7 +129,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -184,7 +186,7 @@ describe("cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { actors_SINGLE: { name_EQ: "Carrie-Anne Moss" } }) { + movies(where: { actors: { single: { name: { eq: "Carrie-Anne Moss" } } } }) { title } } @@ -193,7 +195,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -249,7 +252,7 @@ describe("cypher directive filtering - Relationship", () => { const query = /* GraphQL */ ` query { - movies(where: { actors_SOME: { name_EQ: "Keanu Reeves" } }) { + movies(where: { actors: { some: { name: { eq: "Keanu Reeves" } } } }) { title } } @@ -258,7 +261,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -313,7 +317,7 @@ describe("cypher directive filtering - Relationship", () => { }); const query = /* GraphQL */ ` query { - movies(where: { actors_NONE: { name_EQ: "Keanu Reeves" } }) { + movies(where: { actors: { none: { name: { eq: "Keanu Reeves" } } } }) { title } } @@ -322,7 +326,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -401,8 +406,8 @@ describe("cypher directive filtering - Relationship", () => { movies( where: { OR: [ - { actors_SOME: { name_EQ: "Jada Pinkett Smith" } } - { genres_SOME: { name_EQ: "Romance" } } + { actors: { some: { name: { eq: "Jada Pinkett Smith" } } } } + { genres: { some: { name: { eq: "Romance" } } } } ] } ) { @@ -414,7 +419,8 @@ describe("cypher directive filtering - Relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/interface-relationships/create/connect.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/create/connect.test.ts index b8352c8913..1d525fe249 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/create/connect.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/create/connect.test.ts @@ -63,7 +63,10 @@ describe("Interface Relationships - Create connect", () => { { name: "Actor Name" actedIn: { - connect: { edge: { screenTime: 90 }, where: { node: { title_STARTS_WITH: "The " } } } + connect: { + edge: { screenTime: 90 } + where: { node: { title: { startsWith: "The " } } } + } } } ] @@ -87,7 +90,8 @@ describe("Interface Relationships - Create connect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * @@ -102,7 +106,7 @@ describe("Interface Relationships - Create connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect0_node - MERGE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) + CREATE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) SET this0_actedIn_connect0_relationship.screenTime = $this0_actedIn_connect0_relationship_screenTime } } @@ -120,7 +124,7 @@ describe("Interface Relationships - Create connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect1_node - MERGE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) + CREATE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) SET this0_actedIn_connect1_relationship.screenTime = $this0_actedIn_connect1_relationship_screenTime } } diff --git a/packages/graphql/tests/tck/directives/interface-relationships/create/create.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/create/create.test.ts index b32fb93450..ffcec5a758 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/create/create.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/create/create.test.ts @@ -90,7 +90,8 @@ describe("Interface Relationships - Create create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * diff --git a/packages/graphql/tests/tck/directives/interface-relationships/delete/delete.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/delete/delete.test.ts index 7165d5039e..3d48f22835 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/delete/delete.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/delete/delete.test.ts @@ -61,7 +61,7 @@ describe("Interface Relationships - Delete delete", () => { test("Delete delete an interface relationship", async () => { const query = /* GraphQL */ ` mutation { - deleteActors(delete: { actedIn: { where: { node: { title_STARTS_WITH: "The " } } } }) { + deleteActors(delete: { actedIn: { where: { node: { title: { startsWith: "The " } } } } }) { nodesDeleted relationshipsDeleted } @@ -71,7 +71,8 @@ describe("Interface Relationships - Delete delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH * CALL { WITH * @@ -113,8 +114,8 @@ describe("Interface Relationships - Delete delete", () => { deleteActors( delete: { actedIn: { - where: { node: { title_STARTS_WITH: "The " } } - delete: { actors: { where: { node: { name_EQ: "Actor" } } } } + where: { node: { title: { startsWith: "The " } } } + delete: { actors: { where: { node: { name: { eq: "Actor" } } } } } } } ) { @@ -127,7 +128,8 @@ describe("Interface Relationships - Delete delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH * CALL { WITH * diff --git a/packages/graphql/tests/tck/directives/interface-relationships/read.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/read.test.ts index aea7d5a89e..dbafbb97db 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/read.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/read.test.ts @@ -46,7 +46,6 @@ describe("Interface Relationships", () => { type Actor @node { name: String! - currentlyActingIn: Production @relationship(type: "CURRENTLY_ACTING_IN", direction: OUT) actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } `; @@ -76,7 +75,8 @@ describe("Interface Relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -99,49 +99,6 @@ describe("Interface Relationships", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); }); - test("Simple Interface Relationship Query For Non-Array Field", async () => { - const query = /* GraphQL */ ` - query { - actors { - currentlyActingIn { - title - ... on Movie { - runtime - } - ... on Series { - episodes - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this0:CURRENTLY_ACTING_IN]->(this1:Movie) - WITH this1 { .title, .runtime, __resolveType: \\"Movie\\", __id: id(this1) } AS this1 - RETURN this1 AS var2 - UNION - WITH * - MATCH (this)-[this3:CURRENTLY_ACTING_IN]->(this4:Series) - WITH this4 { .title, .episodes, __resolveType: \\"Series\\", __id: id(this4) } AS this4 - RETURN this4 AS var2 - } - WITH var2 - RETURN head(collect(var2)) AS var2 - } - RETURN this { currentlyActingIn: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - test("Simple Interface Relationship Query with offset and limit", async () => { const query = /* GraphQL */ ` query { @@ -162,7 +119,8 @@ describe("Interface Relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -226,7 +184,8 @@ describe("Interface Relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -254,7 +213,9 @@ describe("Interface Relationships", () => { const query = /* GraphQL */ ` query { actors { - actedInConnection(where: { node: { title_STARTS_WITH: "The " }, edge: { screenTime_GT: 60 } }) { + actedInConnection( + where: { node: { title: { startsWith: "The " } }, edge: { screenTime: { gt: 60 } } } + ) { edges { properties { screenTime @@ -277,7 +238,8 @@ describe("Interface Relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/directives/interface-relationships/update/connect.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/update/connect.test.ts index 2f5bcf11ce..a17ee1c544 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/update/connect.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/update/connect.test.ts @@ -64,7 +64,7 @@ describe("Interface Relationships - Update connect", () => { updateActors( update: { actedIn: { - connect: { edge: { screenTime: 90 }, where: { node: { title_STARTS_WITH: "The " } } } + connect: { edge: { screenTime: 90 }, where: { node: { title: { startsWith: "The " } } } } } } ) { @@ -78,7 +78,8 @@ describe("Interface Relationships - Update connect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -94,7 +95,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actedIn0_connect0_node - MERGE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) + CREATE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) SET this_actedIn0_connect0_relationship.screenTime = $this_actedIn0_connect0_relationship_screenTime } } @@ -117,7 +118,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actedIn0_connect0_node - MERGE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) + CREATE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) SET this_actedIn0_connect0_relationship.screenTime = $this_actedIn0_connect0_relationship_screenTime } } @@ -149,11 +150,11 @@ describe("Interface Relationships - Update connect", () => { actedIn: { connect: { edge: { screenTime: 90 } - where: { node: { title_STARTS_WITH: "The " } } + where: { node: { title: { startsWith: "The " } } } connect: { actors: { edge: { ActedIn: { screenTime: 90 } } - where: { node: { name_EQ: "Actor" } } + where: { node: { name: { eq: "Actor" } } } } } } @@ -170,7 +171,8 @@ describe("Interface Relationships - Update connect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -186,7 +188,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actedIn0_connect0_node - MERGE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) + CREATE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) SET this_actedIn0_connect0_relationship.screenTime = $this_actedIn0_connect0_relationship_screenTime } } @@ -202,7 +204,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this_actedIn0_connect0_node UNWIND connectedNodes as this_actedIn0_connect0_node_actors0_node - MERGE (this_actedIn0_connect0_node)<-[this_actedIn0_connect0_node_actors0_relationship:ACTED_IN]-(this_actedIn0_connect0_node_actors0_node) + CREATE (this_actedIn0_connect0_node)<-[this_actedIn0_connect0_node_actors0_relationship:ACTED_IN]-(this_actedIn0_connect0_node_actors0_node) SET this_actedIn0_connect0_node_actors0_relationship.screenTime = $this_actedIn0_connect0_node_actors0_relationship_ActedIn_screenTime } } @@ -227,7 +229,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actedIn0_connect0_node - MERGE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) + CREATE (this)-[this_actedIn0_connect0_relationship:ACTED_IN]->(this_actedIn0_connect0_node) SET this_actedIn0_connect0_relationship.screenTime = $this_actedIn0_connect0_relationship_screenTime } } @@ -243,7 +245,7 @@ describe("Interface Relationships - Update connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this_actedIn0_connect0_node UNWIND connectedNodes as this_actedIn0_connect0_node_actors0_node - MERGE (this_actedIn0_connect0_node)<-[this_actedIn0_connect0_node_actors0_relationship:ACTED_IN]-(this_actedIn0_connect0_node_actors0_node) + CREATE (this_actedIn0_connect0_node)<-[this_actedIn0_connect0_node_actors0_relationship:ACTED_IN]-(this_actedIn0_connect0_node_actors0_node) SET this_actedIn0_connect0_node_actors0_relationship.screenTime = $this_actedIn0_connect0_node_actors0_relationship_ActedIn_screenTime } } diff --git a/packages/graphql/tests/tck/directives/interface-relationships/update/create.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/update/create.test.ts index 30f89d9c3f..7650b36864 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/update/create.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/update/create.test.ts @@ -87,7 +87,8 @@ describe("Interface Relationships - Update create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this diff --git a/packages/graphql/tests/tck/directives/interface-relationships/update/delete.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/update/delete.test.ts index b4905b4434..f59d8b0ae9 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/update/delete.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/update/delete.test.ts @@ -61,7 +61,7 @@ describe("Interface Relationships - Update delete", () => { test("Update delete an interface relationship", async () => { const query = /* GraphQL */ ` mutation { - updateActors(update: { actedIn: { delete: { where: { node: { title_STARTS_WITH: "The " } } } } }) { + updateActors(update: { actedIn: { delete: { where: { node: { title: { startsWith: "The " } } } } } }) { actors { name } @@ -72,7 +72,8 @@ describe("Interface Relationships - Update delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -145,7 +146,9 @@ describe("Interface Relationships - Update delete", () => { { \\"where\\": { \\"node\\": { - \\"title_STARTS_WITH\\": \\"The \\" + \\"title\\": { + \\"startsWith\\": \\"The \\" + } } } } @@ -167,8 +170,8 @@ describe("Interface Relationships - Update delete", () => { update: { actedIn: { delete: { - where: { node: { title_STARTS_WITH: "The " } } - delete: { actors: { where: { node: { name_EQ: "Actor" } } } } + where: { node: { title: { startsWith: "The " } } } + delete: { actors: { where: { node: { name: { eq: "Actor" } } } } } } } } @@ -183,7 +186,8 @@ describe("Interface Relationships - Update delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -305,7 +309,9 @@ describe("Interface Relationships - Update delete", () => { { \\"where\\": { \\"node\\": { - \\"title_STARTS_WITH\\": \\"The \\" + \\"title\\": { + \\"startsWith\\": \\"The \\" + } } }, \\"delete\\": { @@ -313,7 +319,9 @@ describe("Interface Relationships - Update delete", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor\\" + \\"name\\": { + \\"eq\\": \\"Actor\\" + } } } } diff --git a/packages/graphql/tests/tck/directives/interface-relationships/update/disconnect.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/update/disconnect.test.ts index 850a40bd96..9020475dba 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/update/disconnect.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/update/disconnect.test.ts @@ -61,7 +61,9 @@ describe("Interface Relationships - Update disconnect", () => { test("Update disconnect from an interface relationship", async () => { const query = /* GraphQL */ ` mutation { - updateActors(update: { actedIn: { disconnect: { where: { node: { title_STARTS_WITH: "The " } } } } }) { + updateActors( + update: { actedIn: { disconnect: { where: { node: { title: { startsWith: "The " } } } } } } + ) { actors { name } @@ -72,7 +74,8 @@ describe("Interface Relationships - Update disconnect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -124,7 +127,9 @@ describe("Interface Relationships - Update disconnect", () => { { \\"where\\": { \\"node\\": { - \\"title_STARTS_WITH\\": \\"The \\" + \\"title\\": { + \\"startsWith\\": \\"The \\" + } } } } @@ -146,8 +151,8 @@ describe("Interface Relationships - Update disconnect", () => { update: { actedIn: { disconnect: { - where: { node: { title_STARTS_WITH: "The " } } - disconnect: { actors: { where: { node: { name_EQ: "Actor" } } } } + where: { node: { title: { startsWith: "The " } } } + disconnect: { actors: { where: { node: { name: { eq: "Actor" } } } } } } } } @@ -162,7 +167,8 @@ describe("Interface Relationships - Update disconnect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -239,7 +245,9 @@ describe("Interface Relationships - Update disconnect", () => { { \\"where\\": { \\"node\\": { - \\"title_STARTS_WITH\\": \\"The \\" + \\"title\\": { + \\"startsWith\\": \\"The \\" + } } }, \\"disconnect\\": { @@ -247,7 +255,9 @@ describe("Interface Relationships - Update disconnect", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor\\" + \\"name\\": { + \\"eq\\": \\"Actor\\" + } } } } diff --git a/packages/graphql/tests/tck/directives/interface-relationships/update/update.test.ts b/packages/graphql/tests/tck/directives/interface-relationships/update/update.test.ts index 5b63b35204..e22cedf101 100644 --- a/packages/graphql/tests/tck/directives/interface-relationships/update/update.test.ts +++ b/packages/graphql/tests/tck/directives/interface-relationships/update/update.test.ts @@ -64,7 +64,7 @@ describe("Interface Relationships - Update update", () => { updateActors( update: { actedIn: { - where: { node: { title_EQ: "Old Title" } } + where: { node: { title: { eq: "Old Title" } } } update: { node: { title_SET: "New Title" } } } } @@ -79,7 +79,8 @@ describe("Interface Relationships - Update update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -119,7 +120,9 @@ describe("Interface Relationships - Update update", () => { { \\"where\\": { \\"node\\": { - \\"title_EQ\\": \\"Old Title\\" + \\"title\\": { + \\"eq\\": \\"Old Title\\" + } } }, \\"update\\": { @@ -143,7 +146,7 @@ describe("Interface Relationships - Update update", () => { updateActors( update: { actedIn: { - where: { node: { title_EQ: "Old Title" } } + where: { node: { title: { eq: "Old Title" } } } update: { node: { actors: { update: { node: { name_SET: "New Actor Name" } } } } } } } @@ -158,7 +161,8 @@ describe("Interface Relationships - Update update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -210,7 +214,9 @@ describe("Interface Relationships - Update update", () => { { \\"where\\": { \\"node\\": { - \\"title_EQ\\": \\"Old Title\\" + \\"title\\": { + \\"eq\\": \\"Old Title\\" + } } }, \\"update\\": { diff --git a/packages/graphql/tests/tck/directives/node/node-additional-labels.test.ts b/packages/graphql/tests/tck/directives/node/node-additional-labels.test.ts index d8dff96281..0074d93355 100644 --- a/packages/graphql/tests/tck/directives/node/node-additional-labels.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-additional-labels.test.ts @@ -55,7 +55,8 @@ describe("Node directive with additionalLabels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film:Multimedia) + "CYPHER 5 + MATCH (this:Film:Multimedia) RETURN this { .title } AS this" `); @@ -77,10 +78,12 @@ describe("Node directive with additionalLabels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film:Multimedia) + "CYPHER 5 + MATCH (this:Film:Multimedia) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor:Person) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -109,7 +112,8 @@ describe("Node directive with additionalLabels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Film:Multimedia) @@ -165,7 +169,7 @@ describe("Node directive with additionalLabels", () => { test("Delete Movie with additional additionalLabels", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: "123" }) { + deleteMovies(where: { id: { eq: "123" } }) { nodesDeleted } } @@ -174,7 +178,8 @@ describe("Node directive with additionalLabels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film:Multimedia) + "CYPHER 5 + MATCH (this:Film:Multimedia) WHERE this.id = $param0 DETACH DELETE this" `); @@ -189,7 +194,7 @@ describe("Node directive with additionalLabels", () => { test("Update Movie with additional labels", async () => { const query = /* GraphQL */ ` mutation { - updateMovies(where: { id_EQ: "1" }, update: { id_SET: "2" }) { + updateMovies(where: { id: { eq: "1" } }, update: { id_SET: "2" }) { movies { id } @@ -200,7 +205,8 @@ describe("Node directive with additionalLabels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film:Multimedia) + "CYPHER 5 + MATCH (this:Film:Multimedia) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" diff --git a/packages/graphql/tests/tck/directives/node/node-label-interface.test.ts b/packages/graphql/tests/tck/directives/node/node-label-interface.test.ts index 040f6b97ae..6a117c2da3 100644 --- a/packages/graphql/tests/tck/directives/node/node-label-interface.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-label-interface.test.ts @@ -49,8 +49,8 @@ describe("Node directive with interface", () => { test("Read Interface", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "some title" }) { - search(where: { name_EQ: "Horror" }, offset: 1, limit: 10) { + movies(where: { title: { eq: "some title" } }) { + search(where: { name: { eq: "Horror" } }, offset: 1, limit: 10) { ... on Movie { title } @@ -65,7 +65,8 @@ describe("Node directive with interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/directives/node/node-label-jwt.test.ts b/packages/graphql/tests/tck/directives/node/node-label-jwt.test.ts index a405043082..ad17cc8f21 100644 --- a/packages/graphql/tests/tck/directives/node/node-label-jwt.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-label-jwt.test.ts @@ -60,7 +60,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) RETURN this { .title } AS this" `); @@ -70,9 +71,9 @@ describe("Label in Node directive", () => { test("Select Movie with label Film from Actors with additionalLabels", async () => { const query = /* GraphQL */ ` query { - actors(where: { age_GT: 10 }) { + actors(where: { age: { gt: 10 } }) { name - movies(where: { title_EQ: "terminator" }) { + movies(where: { title: { eq: "terminator" } }) { title } } @@ -83,12 +84,14 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor:Person) + "CYPHER 5 + MATCH (this:Actor:Person) WHERE this.age > $param0 CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Film) WHERE this1.title = $param1 + WITH DISTINCT this1 WITH this1 { .title } AS this1 RETURN collect(this1) AS var2 } @@ -117,11 +120,12 @@ describe("Label in Node directive", () => { } `; - const token = createBearerToken("secret", { movielabel: "Film", personlabel: "Person" }); + const token = createBearerToken("secret", { movielabel: "Film", personlabel: "Person" }); const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Film) diff --git a/packages/graphql/tests/tck/directives/node/node-label-union.test.ts b/packages/graphql/tests/tck/directives/node/node-label-union.test.ts index 59053d86e9..3812cb3203 100644 --- a/packages/graphql/tests/tck/directives/node/node-label-union.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-label-union.test.ts @@ -46,9 +46,9 @@ describe("Node directive with unions", () => { test("Read Unions", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "some title" }) { + movies(where: { title: { eq: "some title" } }) { search( - where: { Movie: { title_EQ: "The Matrix" }, Genre: { name_EQ: "Horror" } } + where: { Movie: { title: { eq: "The Matrix" } }, Genre: { name: { eq: "Horror" } } } offset: 1 limit: 10 ) { @@ -66,7 +66,8 @@ describe("Node directive with unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.title = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/directives/node/node-label.test.ts b/packages/graphql/tests/tck/directives/node/node-label.test.ts index 18501a87b0..8acecbd650 100644 --- a/packages/graphql/tests/tck/directives/node/node-label.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-label.test.ts @@ -55,7 +55,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) RETURN this { .title } AS this" `); @@ -77,10 +78,12 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -109,7 +112,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) @@ -143,7 +147,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Film) @@ -184,7 +189,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Film) @@ -240,7 +246,7 @@ describe("Label in Node directive", () => { test("Update Movie with label film", async () => { const query = /* GraphQL */ ` mutation { - updateMovies(where: { id_EQ: "1" }, update: { id_SET: "2" }) { + updateMovies(where: { id: { eq: "1" } }, update: { id_SET: "2" }) { movies { id } @@ -251,7 +257,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -270,10 +277,13 @@ describe("Label in Node directive", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: [ - { where: { node: { name_EQ: "old name" } }, update: { node: { name_SET: "new name" } } } + { + where: { node: { name: { eq: "old name" } } } + update: { node: { name_SET: "new name" } } + } ] } ) { @@ -287,7 +297,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 WITH this CALL { @@ -312,7 +323,9 @@ describe("Label in Node directive", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"old name\\" + \\"name\\": { + \\"eq\\": \\"old name\\" + } } }, \\"update\\": { @@ -334,8 +347,8 @@ describe("Label in Node directive", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } - update: { actors: { connect: [{ where: { node: { name_EQ: "Daniel" } } }] } } + where: { id: { eq: "1" } } + update: { actors: { connect: [{ where: { node: { name: { eq: "Daniel" } } } }] } } ) { movies { id @@ -347,7 +360,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 WITH * CALL { @@ -361,7 +375,7 @@ describe("Label in Node directive", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[:ACTED_IN]-(this_actors0_connect0_node) + CREATE (this)<-[:ACTED_IN]-(this_actors0_connect0_node) } } WITH this, this_actors0_connect0_node @@ -383,8 +397,8 @@ describe("Label in Node directive", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } - update: { actors: { disconnect: [{ where: { node: { name_EQ: "Daniel" } } }] } } + where: { id: { eq: "1" } } + update: { actors: { disconnect: [{ where: { node: { name: { eq: "Daniel" } } } }] } } ) { movies { id @@ -396,7 +410,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 WITH this CALL { @@ -427,7 +442,9 @@ describe("Label in Node directive", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Daniel\\" + \\"name\\": { + \\"eq\\": \\"Daniel\\" + } } } } @@ -445,7 +462,7 @@ describe("Label in Node directive", () => { test("Delete Movie with custom label", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: "123" }) { + deleteMovies(where: { id: { eq: "123" } }) { nodesDeleted } } @@ -454,7 +471,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 DETACH DELETE this" `); @@ -470,8 +488,8 @@ describe("Label in Node directive", () => { const query = /* GraphQL */ ` mutation { deleteMovies( - where: { id_EQ: 123 } - delete: { actors: { where: { node: { name_EQ: "Actor to delete" } } } } + where: { id: { eq: 123 } } + delete: { actors: { where: { node: { name: { eq: "Actor to delete" } } } } } ) { nodesDeleted } @@ -481,7 +499,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE this.id = $param0 WITH * CALL { @@ -510,7 +529,7 @@ describe("Label in Node directive", () => { test("Admin Deletes Post", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { actors_SOME: { name_EQ: "tom" } }) { + deleteMovies(where: { actors: { some: { name: { eq: "tom" } } } }) { nodesDeleted } } @@ -519,7 +538,8 @@ describe("Label in Node directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[:ACTED_IN]-(this0:Person) WHERE this0.name = $param0 diff --git a/packages/graphql/tests/tck/directives/node/node-with-auth-projection.test.ts b/packages/graphql/tests/tck/directives/node/node-with-auth-projection.test.ts index e9c2749aa1..8706460771 100644 --- a/packages/graphql/tests/tck/directives/node/node-with-auth-projection.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-with-auth-projection.test.ts @@ -30,7 +30,7 @@ describe("Cypher Auth Projection On Connections", () => { typeDefs = /* GraphQL */ ` type Post @node(labels: ["Comment"]) { content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } type User @node(labels: ["Person"]) { @@ -39,9 +39,11 @@ describe("Cypher Auth Projection On Connections", () => { posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) } - extend type User @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type User @authorization(validate: [{ when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }]) extend type Post - @authorization(validate: [{ when: [BEFORE], where: { node: { creator: { id_EQ: "$jwt.sub" } } } }]) + @authorization( + validate: [{ when: [BEFORE], where: { node: { creator: { some: { id: { eq: "$jwt.sub" } } } } } }] + ) `; neoSchema = new Neo4jGraphQL({ @@ -72,27 +74,28 @@ describe("Cypher Auth Projection On Connections", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[this0:HAS_POST]->(this1:Comment) - OPTIONAL MATCH (this1)<-[:HAS_POST]-(this2:Person) - WITH *, count(this2) AS var3 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:HAS_POST]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var4 + RETURN collect({ node: { content: this1.content, __resolveType: \\"Post\\" } }) AS var3 } - RETURN { edges: var4, totalCount: totalCount } AS var5 + RETURN { edges: var3, totalCount: totalCount } AS var4 } - RETURN this { .name, postsConnection: var5 } AS this" + RETURN this { .name, postsConnection: var4 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/directives/node/node-with-auth.test.ts b/packages/graphql/tests/tck/directives/node/node-with-auth.test.ts index be50bb08ae..811474e98c 100644 --- a/packages/graphql/tests/tck/directives/node/node-with-auth.test.ts +++ b/packages/graphql/tests/tck/directives/node/node-with-auth.test.ts @@ -35,11 +35,11 @@ describe("Node Directive", () => { type Post @node(labels: ["Comment"]) { id: ID content: String - creator: User! @relationship(type: "HAS_POST", direction: IN) + creator: [User!]! @relationship(type: "HAS_POST", direction: IN) } extend type Post - @authorization(validate: [{ operations: [DELETE], where: { jwt: { roles_INCLUDES: "admin" } } }]) + @authorization(validate: [{ operations: [DELETE], where: { jwt: { roles: { includes: "admin" } } } }]) type User @node(labels: ["Person"]) { id: ID @@ -53,7 +53,7 @@ describe("Node Directive", () => { { operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] when: [BEFORE] - where: { node: { id_EQ: "$jwt.sub" } } + where: { node: { id: { eq: "$jwt.sub" } } } } ] ) @@ -80,7 +80,8 @@ describe("Node Directive", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .id } AS this" @@ -102,7 +103,7 @@ describe("Node Directive", () => { test("Admin Deletes Post", async () => { const query = /* GraphQL */ ` mutation { - deletePosts(where: { creator: { id_EQ: "123" } }) { + deletePosts(where: { creator: { some: { id: { eq: "123" } } } }) { nodesDeleted } } @@ -112,10 +113,12 @@ describe("Node Directive", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Comment) - OPTIONAL MATCH (this)<-[:HAS_POST]-(this0:Person) - WITH *, count(this0) AS var1 - WHERE ((var1 <> 0 AND this0.id = $param0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + "CYPHER 5 + MATCH (this:Comment) + WHERE (EXISTS { + MATCH (this)<-[:HAS_POST]-(this0:Person) + WHERE this0.id = $param0 + } AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) DETACH DELETE this" `); diff --git a/packages/graphql/tests/tck/directives/plural.test.ts b/packages/graphql/tests/tck/directives/plural.test.ts index f65f1de0a7..63fd60b5a7 100644 --- a/packages/graphql/tests/tck/directives/plural.test.ts +++ b/packages/graphql/tests/tck/directives/plural.test.ts @@ -48,7 +48,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tech) + "CYPHER 5 + MATCH (this:Tech) RETURN this { .name } AS this" `); @@ -67,7 +68,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this:Tech) RETURN count(this) AS var0 } @@ -91,7 +93,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Tech) @@ -127,7 +130,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tech) + "CYPHER 5 + MATCH (this:Tech) SET this.name = $this_update_name_SET RETURN collect(DISTINCT this { .name }) AS data" `); @@ -143,7 +147,7 @@ describe("Plural directive", () => { test("Delete Tech with plural techs using aggregation", async () => { const query = /* GraphQL */ ` mutation { - deleteTechs(where: { name_EQ: "Matrix" }) { + deleteTechs(where: { name: { eq: "Matrix" } }) { nodesDeleted } } @@ -152,7 +156,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tech) + "CYPHER 5 + MATCH (this:Tech) WHERE this.name = $param0 DETACH DELETE this" `); @@ -176,7 +181,8 @@ describe("Plural directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tech) + "CYPHER 5 + MATCH (this:Tech) RETURN this { .name } AS this" `); diff --git a/packages/graphql/tests/tck/directives/relationship.test.ts b/packages/graphql/tests/tck/directives/relationship.test.ts index 2d484cf4da..acae1abca6 100644 --- a/packages/graphql/tests/tck/directives/relationship.test.ts +++ b/packages/graphql/tests/tck/directives/relationship.test.ts @@ -41,7 +41,6 @@ describe("Cypher relationship", () => { id: ID title: String actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - topActor: Actor! @relationship(type: "TOP_ACTOR", direction: OUT) } `; @@ -50,34 +49,6 @@ describe("Cypher relationship", () => { }); }); - test("Simple relation", async () => { - const query = /* GraphQL */ ` - { - movies { - title - topActor { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - CALL { - WITH this - MATCH (this)-[this0:TOP_ACTOR]->(this1:Actor) - WITH this1 { .name } AS this1 - RETURN head(collect(this1)) AS var2 - } - RETURN this { .title, topActor: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - test("Many relation", async () => { const query = /* GraphQL */ ` { @@ -93,10 +64,12 @@ describe("Cypher relationship", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -105,87 +78,4 @@ describe("Cypher relationship", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); }); - - test("Nested relation", async () => { - const query = /* GraphQL */ ` - { - movies { - title - topActor { - name - movies { - title - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - CALL { - WITH this - MATCH (this)-[this0:TOP_ACTOR]->(this1:Actor) - CALL { - WITH this1 - MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) - WITH this3 { .title } AS this3 - RETURN collect(this3) AS var4 - } - WITH this1 { .name, movies: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .title, topActor: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("Nested relation with params", async () => { - const query = /* GraphQL */ ` - { - movies(where: { title_EQ: "some title" }) { - title - topActor(where: { name_EQ: "top actor" }) { - name - movies(where: { title_EQ: "top actor movie" }) { - title - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.title = $param0 - CALL { - WITH this - MATCH (this)-[this0:TOP_ACTOR]->(this1:Actor) - WHERE this1.name = $param1 - CALL { - WITH this1 - MATCH (this1)-[this2:ACTED_IN]->(this3:Movie) - WHERE this3.title = $param2 - WITH this3 { .title } AS this3 - RETURN collect(this3) AS var4 - } - WITH this1 { .name, movies: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .title, topActor: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"some title\\", - \\"param1\\": \\"top actor\\", - \\"param2\\": \\"top actor movie\\" - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/directives/vector/auth.test.ts b/packages/graphql/tests/tck/directives/vector/auth.test.ts index bcf38c8a69..de85dbb9bf 100644 --- a/packages/graphql/tests/tck/directives/vector/auth.test.ts +++ b/packages/graphql/tests/tck/directives/vector/auth.test.ts @@ -49,7 +49,7 @@ describe("Cypher -> vector -> Auth", () => { type Movie @node @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) - @authorization(filter: [{ where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }]) { + @authorization(filter: [{ where: { node: { director: { some: { id: { eq: "$jwt.sub" } } } } } }]) { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) } @@ -85,7 +85,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND ($isAuthenticated = true AND EXISTS { MATCH (this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) @@ -248,7 +249,7 @@ describe("Cypher -> vector -> Auth", () => { type Movie @node @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) - @authorization(validate: [{ when: [BEFORE], where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }]) { + @authorization(validate: [{ when: [BEFORE], where: { node: { director: { some: { id: { eq: "$jwt.sub" } } } } } }]) { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) } @@ -284,7 +285,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) @@ -448,7 +450,7 @@ describe("Cypher -> vector -> Auth", () => { @node @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) @authorization( - validate: [{ when: [BEFORE], where: { node: { director_ALL: { id_EQ: "$jwt.sub" } } } }] + validate: [{ when: [BEFORE], where: { node: { director: { all: { id: { eq: "$jwt.sub" } } } } } }] ) { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) @@ -485,7 +487,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) @@ -653,7 +656,7 @@ describe("Cypher -> vector -> Auth", () => { @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) @authorization( validate: [ - { when: [BEFORE], where: { node: { directorConnection_SOME: { node: { id_EQ: "$jwt.sub" } } } } } + { when: [BEFORE], where: { node: { directorConnection: { some: { node: { id: { eq: "$jwt.sub" } } } } } } } ] ) { title: String @@ -691,7 +694,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) @@ -858,7 +862,7 @@ describe("Cypher -> vector -> Auth", () => { validate: [ { when: [BEFORE] - where: { node: { directorConnection_ALL: { node: { id_EQ: "$jwt.sub" } } } } + where: { node: { directorConnection: { all: { node: { id: { eq: "$jwt.sub" } } } } } } } ] ) { @@ -897,7 +901,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) @@ -1065,7 +1070,7 @@ describe("Cypher -> vector -> Auth", () => { @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) @authorization( validate: [ - { when: [BEFORE], where: { node: { directorConnection_SOME: { edge: { year_EQ: 2020 } } } } } + { when: [BEFORE], where: { node: { directorConnection: { some: { edge: { year: { eq: 2020 } } } } } } } ] ) { title: String @@ -1107,7 +1112,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($param3 IS NOT NULL AND this2.year = $param3) @@ -1269,7 +1275,7 @@ describe("Cypher -> vector -> Auth", () => { @vector(indexes: [{ indexName: "movie_index", embeddingProperty: "movieVector", queryName: "${queryName}" }]) @authorization( validate: [ - { when: [BEFORE], where: { node: { directorConnection_ALL: { edge: { year_EQ: 2020 } } } } } + { when: [BEFORE], where: { node: { directorConnection: { all: { edge: { year: { eq: 2020 } } } } } } } ] ) { title: String @@ -1311,7 +1317,8 @@ describe("Cypher -> vector -> Auth", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { MATCH (this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($param3 IS NOT NULL AND this2.year = $param3) diff --git a/packages/graphql/tests/tck/directives/vector/match.test.ts b/packages/graphql/tests/tck/directives/vector/match.test.ts index 4654146f37..92efe42df9 100644 --- a/packages/graphql/tests/tck/directives/vector/match.test.ts +++ b/packages/graphql/tests/tck/directives/vector/match.test.ts @@ -75,18 +75,19 @@ describe("Vector index match", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH collect({ node: this0, score: var1 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this0, edge.score AS var1 - RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 - } - RETURN { edges: var2, totalCount: totalCount } AS this" - `); + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + WHERE $param1 IN labels(this0) + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -228,7 +229,7 @@ describe("Vector index match", () => { test("simple match with single property and score and filter", async () => { const query = /* GraphQL */ ` query MovieVectorQuery($vector: [Float!]!) { - ${queryName}(vector: $vector, where: { node: { released_GT: 2000 } }) { + ${queryName}(vector: $vector, where: { node: { released: { gt: 2000 } } }) { edges { cursor score @@ -248,7 +249,8 @@ describe("Vector index match", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND this0.released > $param2) WITH collect({ node: this0, score: var1 }) AS edges WITH edges, size(edges) AS totalCount @@ -423,7 +425,8 @@ describe("Vector index match", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) WITH collect({ node: this0, score: var1 }) AS edges WITH edges, size(edges) AS totalCount diff --git a/packages/graphql/tests/tck/directives/vector/phrase.test.ts b/packages/graphql/tests/tck/directives/vector/phrase.test.ts index 0047bfdf62..7695c0de1c 100644 --- a/packages/graphql/tests/tck/directives/vector/phrase.test.ts +++ b/packages/graphql/tests/tck/directives/vector/phrase.test.ts @@ -85,7 +85,8 @@ describe("phrase input - genAI plugin", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "WITH genai.vector.encode($param0, \\"OpenAI\\", { token: \\"my-token\\", model: \\"my-model\\", dimensions: 256 }) AS var0 + "CYPHER 5 + WITH genai.vector.encode($param0, \\"OpenAI\\", { token: \\"my-token\\", model: \\"my-model\\", dimensions: 256 }) AS var0 CALL db.index.vector.queryNodes(\\"movie_index\\", 4, var0) YIELD node AS this1, score AS var2 WHERE $param1 IN labels(this1) WITH collect({ node: this1, score: var2 }) AS edges diff --git a/packages/graphql/tests/tck/directives/vector/score.test.ts b/packages/graphql/tests/tck/directives/vector/score.test.ts index 290a4a38c1..bb5972a865 100644 --- a/packages/graphql/tests/tck/directives/vector/score.test.ts +++ b/packages/graphql/tests/tck/directives/vector/score.test.ts @@ -73,7 +73,8 @@ describe("Cypher -> vector -> Score", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND var1 >= $param2) WITH collect({ node: this0, score: var1 }) AS edges WITH edges, size(edges) AS totalCount @@ -245,7 +246,8 @@ describe("Cypher -> vector -> Score", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) WITH collect({ node: this0, score: var1 }) AS edges WITH edges, size(edges) AS totalCount @@ -418,7 +420,8 @@ describe("Cypher -> vector -> Score", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.vector.queryNodes(\\"movie_index\\", 4, $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) WITH collect({ node: this0, score: var1 }) AS edges WITH edges, size(edges) AS totalCount diff --git a/packages/graphql/tests/tck/federation/authorization.test.ts b/packages/graphql/tests/tck/federation/authorization.test.ts index d16fd4d8dd..b64e228180 100644 --- a/packages/graphql/tests/tck/federation/authorization.test.ts +++ b/packages/graphql/tests/tck/federation/authorization.test.ts @@ -32,7 +32,10 @@ describe("Federation and authorization", () => { const typeDefs = /* GraphQL */ ` extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"]) - type User @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) @key(fields: "id") @node { + type User + @authorization(filter: [{ where: { node: { id: { eq: "$jwt.sub" } } } }]) + @key(fields: "id") + @node { id: ID! name: String! } @@ -65,7 +68,8 @@ describe("Federation and authorization", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) RETURN this { .id, .name } AS this" @@ -95,7 +99,7 @@ describe("Federation and authorization", () => { type User @key(fields: "id") @node { id: ID! name: String! - password: String! @authorization(filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }]) + password: String! @authorization(filter: [{ where: { node: { id: { eq: "$jwt.sub" } } } }]) } `; @@ -127,7 +131,8 @@ describe("Federation and authorization", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE (this.id = $param0 AND ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this.id = $jwt.sub))) RETURN this { .id, .name, .password } AS this" @@ -161,7 +166,7 @@ describe("Federation and authorization", () => { type Post @node - @authorization(filter: [{ where: { node: { authorsAggregate: { count_GT: 2 } } } }]) + @authorization(filter: [{ where: { node: { authorsAggregate: { count: { gt: 2 } } } } }]) @key(fields: "id") { id: ID! content: String! @@ -196,7 +201,8 @@ describe("Federation and authorization", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:AUTHORED]-(this1:User) diff --git a/packages/graphql/tests/tck/fragments.test.ts b/packages/graphql/tests/tck/fragments.test.ts index 3b65e42a28..6ea05bb074 100644 --- a/packages/graphql/tests/tck/fragments.test.ts +++ b/packages/graphql/tests/tck/fragments.test.ts @@ -31,7 +31,7 @@ describe("Cypher Fragment", () => { } type User implements Entity @node { - id: ID! @id @unique + id: ID! @id username: String! owns: [OwnableType!]! @relationship(type: "OWNS", direction: OUT) } @@ -40,17 +40,17 @@ describe("Cypher Fragment", () => { interface Ownable { id: ID! - owner: User + owner: [User!]! } type Tile implements Ownable @node { - id: ID! @id @unique - owner: User! @relationship(type: "OWNS", direction: IN) + id: ID! @id + owner: [User!]! @relationship(type: "OWNS", direction: IN) } type Character implements Ownable @node { - id: ID! @id @unique - owner: User! @relationship(type: "OWNS", direction: IN) + id: ID! @id + owner: [User!]! @relationship(type: "OWNS", direction: IN) } `; @@ -76,7 +76,8 @@ describe("Cypher Fragment", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .id, .username } AS this" `); @@ -101,7 +102,8 @@ describe("Cypher Fragment", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this CALL { @@ -141,7 +143,8 @@ describe("Cypher Fragment", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) RETURN this { .username, .id } AS this" `); @@ -186,7 +189,7 @@ describe("Cypher Fragment", () => { const query = /* GraphQL */ ` query { - actors(where: { name_EQ: "Keanu" }) { + actors(where: { name: { eq: "Keanu" } }) { name actedIn { ...FragmentA @@ -209,7 +212,8 @@ describe("Cypher Fragment", () => { const result = await translateQuery(testNeoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/fulltext/aggregate.test.ts b/packages/graphql/tests/tck/fulltext/aggregate.test.ts deleted file mode 100644 index 7e8debb2e7..0000000000 --- a/packages/graphql/tests/tck/fulltext/aggregate.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Cypher -> fulltext -> Aggregate", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) @node { - title: String - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("simple aggregate with single fulltext property", async () => { - const query = /* GraphQL */ ` - query { - moviesAggregate(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - count - } - } - `; - - const result = await translateQuery(neoSchema, query, {}); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - RETURN count(this0) AS var2 - } - RETURN { count: var2 }" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"something AND something\\", - \\"param1\\": \\"Movie\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/fulltext/auth.test.ts b/packages/graphql/tests/tck/fulltext/auth.test.ts index d04be1422e..4c73c2ce13 100644 --- a/packages/graphql/tests/tck/fulltext/auth.test.ts +++ b/packages/graphql/tests/tck/fulltext/auth.test.ts @@ -37,111 +37,65 @@ describe("Cypher -> fulltext -> Auth", () => { } }); - describe("4.4", () => { - test("simple match with auth where", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization(filter: [{ where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }]) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "jwt": Object { - "roles": Array [], - "sub": "my-sub", - }, - "param0": "something AND something", - "param1": "Movie", - } - `); + test("simple match with auth where", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization(filter: [{ where: { node: { director: { some: { id: { eq: "$jwt.sub" } } } } } }]) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) + } + + type Person @node { + id: ID + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, }); - test("simple match with auth allow", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [{ when: [BEFORE], where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + })) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var3 + } + RETURN { edges: var3, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "jwt": Object { @@ -152,119 +106,69 @@ describe("Cypher -> fulltext -> Auth", () => { "param1": "Movie", } `); - }); - - test("simple match with auth allow ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [{ when: [BEFORE], where: { node: { director_ALL: { id_EQ: "$jwt.sub" } } } }] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:DIRECTED]-(this2:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); + }); - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "jwt": Object { - "roles": Array [], - "sub": "my-sub", - }, - "param0": "something AND something", - "param1": "Movie", - } - `); + test("simple match with auth allow", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [{ when: [BEFORE], where: { node: { director: { some: { id: { eq: "$jwt.sub" } } } } } }] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) + } + + type Person @node { + id: ID + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, }); - test("simple match with auth allow on connection node", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_SOME: { node: { id_EQ: "$jwt.sub" } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this3:DIRECTED]-(this2:Person) WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var3 + } + RETURN { edges: var3, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "jwt": Object { @@ -275,60 +179,72 @@ describe("Cypher -> fulltext -> Auth", () => { "param1": "Movie", } `); - }); - - test("simple match with auth allow on connection node ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_ALL: { node: { id_EQ: "$jwt.sub" } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; + }); - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); + test("simple match with auth allow ALL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [{ when: [BEFORE], where: { node: { director: { all: { id: { eq: "$jwt.sub" } } } } } }] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) + } + + type Person @node { + id: ID + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this3:DIRECTED]-(this2:Person) WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } AND NOT (EXISTS { + MATCH (this0)<-[:DIRECTED]-(this2:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var3 + } + RETURN { edges: var3, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "jwt": Object { @@ -339,247 +255,74 @@ describe("Cypher -> fulltext -> Auth", () => { "param1": "Movie", } `); - }); - - test("simple match with auth allow on connection edge", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_SOME: { edge: { year_EQ: 2020 } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") - } - - type Person @node { - id: ID - } - - type Directed @relationshipProperties { - year: Int - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this2:DIRECTED]-(this3:Person) WHERE ($param3 IS NOT NULL AND this2.year = $param3) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "param0": "something AND something", - "param1": "Movie", - "param3": 2020, - } - `); - }); - - test("simple match with auth allow on connection edge ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { when: [BEFORE], where: { node: { directorConnection_ALL: { edge: { year_EQ: 2020 } } } } } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") - } - - type Person @node { - id: ID - } - - type Directed @relationshipProperties { - year: Int - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[this2:DIRECTED]-(this3:Person) WHERE NOT ($param3 IS NOT NULL AND this2.year = $param3) | 1]) = 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "param0": "something AND something", - "param1": "Movie", - "param3": 2020, - } - `); - }); }); - describe("5", () => { - test("simple match with auth where", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization(filter: [{ where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }]) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE ($isAuthenticated = true AND EXISTS { - MATCH (this0)<-[:DIRECTED]-(this2:Person) - WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) - }) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "jwt": Object { - "roles": Array [], - "sub": "my-sub", - }, - "param0": "something AND something", - "param1": "Movie", - } - `); + test("simple match with auth allow on connection node", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [ + { + when: [BEFORE] + where: { node: { directorConnection: { some: { node: { id: { eq: "$jwt.sub" } } } } } } + } + ] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) + } + + type Person @node { + id: ID + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, }); - test("simple match with auth allow", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [{ when: [BEFORE], where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this0)<-[:DIRECTED]-(this2:Person) - WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var4 + } + RETURN { edges: var4, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "jwt": Object { @@ -590,128 +333,77 @@ describe("Cypher -> fulltext -> Auth", () => { "param1": "Movie", } `); - }); - - test("simple match with auth allow ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [{ when: [BEFORE], where: { node: { director_ALL: { id_EQ: "$jwt.sub" } } } }] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this0)<-[:DIRECTED]-(this2:Person) - WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) - } AND NOT (EXISTS { - MATCH (this0)<-[:DIRECTED]-(this2:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); + }); - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "jwt": Object { - "roles": Array [], - "sub": "my-sub", - }, - "param0": "something AND something", - "param1": "Movie", - } - `); + test("simple match with auth allow on connection node ALL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [ + { + when: [BEFORE] + where: { node: { directorConnection: { all: { node: { id: { eq: "$jwt.sub" } } } } } } + } + ] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN) + } + + type Person @node { + id: ID + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, }); - test("simple match with auth allow on connection node", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_SOME: { node: { id_EQ: "$jwt.sub" } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + } AND NOT (EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE NOT ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var4 + } + RETURN { edges: var4, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "jwt": Object { @@ -722,137 +414,78 @@ describe("Cypher -> fulltext -> Auth", () => { "param1": "Movie", } `); - }); - - test("simple match with auth allow on connection node ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_ALL: { node: { id_EQ: "$jwt.sub" } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN) - } - - type Person @node { - id: ID - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title - } - } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) - } AND NOT (EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE NOT ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); + }); - expect(result.params).toMatchInlineSnapshot(` - Object { - "isAuthenticated": true, - "jwt": Object { - "roles": Array [], - "sub": "my-sub", - }, - "param0": "something AND something", - "param1": "Movie", - } - `); + test("simple match with auth allow on connection edge", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [ + { + when: [BEFORE] + where: { node: { directorConnection: { some: { edge: { year: { eq: 2020 } } } } } } + } + ] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") + } + + type Person @node { + id: ID + } + + type Directed @relationshipProperties { + year: Int + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, }); - test("simple match with auth allow on connection edge", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { - when: [BEFORE] - where: { node: { directorConnection_SOME: { edge: { year_EQ: 2020 } } } } - } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") - } - - type Person @node { - id: ID - } - - type Directed @relationshipProperties { - year: Int - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; - - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); - - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE ($param3 IS NOT NULL AND this2.year = $param3) - }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($param3 IS NOT NULL AND this2.year = $param3) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var4 + } + RETURN { edges: var4, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "param0": "something AND something", @@ -860,67 +493,81 @@ describe("Cypher -> fulltext -> Auth", () => { "param3": 2020, } `); - }); - - test("simple match with auth allow on connection edge ALL", async () => { - const typeDefs = /* GraphQL */ ` - type Movie - @node - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) - @authorization( - validate: [ - { when: [BEFORE], where: { node: { directorConnection_ALL: { edge: { year_EQ: 2020 } } } } } - ] - ) { - title: String - director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") - } - - type Person @node { - id: ID - } - - type Directed @relationshipProperties { - year: Int - } - `; - - const secret = "shh-its-a-secret"; - - const sub = "my-sub"; + }); - const neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: secret } }, - }); + test("simple match with auth allow on connection edge ALL", async () => { + const typeDefs = /* GraphQL */ ` + type Movie + @node + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @authorization( + validate: [ + { + when: [BEFORE] + where: { node: { directorConnection: { all: { edge: { year: { eq: 2020 } } } } } } + } + ] + ) { + title: String + director: [Person!]! @relationship(type: "DIRECTED", direction: IN, properties: "Directed") + } + + type Person @node { + id: ID + } + + type Directed @relationshipProperties { + year: Int + } + `; + + const secret = "shh-its-a-secret"; + + const sub = "my-sub"; + + const neoSchema = new Neo4jGraphQL({ + typeDefs, + features: { authorization: { key: secret } }, + }); - const query = /* GraphQL */ ` - query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + const query = /* GraphQL */ ` + query { + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } } } - `; - - const token = createBearerToken(secret, { sub }); - - const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 - WHERE $param1 IN labels(this0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE ($param3 IS NOT NULL AND this2.year = $param3) - } AND NOT (EXISTS { - MATCH (this0)<-[this2:DIRECTED]-(this3:Person) - WHERE NOT ($param3 IS NOT NULL AND this2.year = $param3) - }))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 { .title } AS this" - `); - - expect(result.params).toMatchInlineSnapshot(` + } + `; + + const token = createBearerToken(secret, { sub }); + + const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5" }); + + expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + WHERE ($param1 IN labels(this0) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE ($param3 IS NOT NULL AND this2.year = $param3) + } AND NOT (EXISTS { + MATCH (this0)<-[this2:DIRECTED]-(this3:Person) + WHERE NOT ($param3 IS NOT NULL AND this2.year = $param3) + }))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var4 + } + RETURN { edges: var4, totalCount: totalCount } AS this" + `); + + expect(result.params).toMatchInlineSnapshot(` Object { "isAuthenticated": true, "param0": "something AND something", @@ -928,6 +575,5 @@ describe("Cypher -> fulltext -> Auth", () => { "param3": 2020, } `); - }); }); }); diff --git a/packages/graphql/tests/tck/fulltext/match.test.ts b/packages/graphql/tests/tck/fulltext/match.test.ts index f1fa1d431a..4469616ed1 100644 --- a/packages/graphql/tests/tck/fulltext/match.test.ts +++ b/packages/graphql/tests/tck/fulltext/match.test.ts @@ -26,7 +26,9 @@ describe("Cypher -> fulltext -> Match", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` - type Movie @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) @node { + type Movie + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @node { title: String } `; @@ -39,8 +41,12 @@ describe("Cypher -> fulltext -> Match", () => { test("simple match with single fulltext property", async () => { const query = /* GraphQL */ ` query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } + } } } `; @@ -48,9 +54,18 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) - RETURN this0 { .title } AS this" + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -64,11 +79,12 @@ describe("Cypher -> fulltext -> Match", () => { test("match with where and single fulltext property", async () => { const query = /* GraphQL */ ` query { - movies( - fulltext: { MovieTitle: { phrase: "something AND something" } } - where: { title_EQ: "some-title" } - ) { - title + moviesByTitle(phrase: "something AND something", where: { node: { title: { eq: "some-title" } } }) { + edges { + node { + title + } + } } } `; @@ -76,9 +92,18 @@ describe("Cypher -> fulltext -> Match", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND this0.title = $param2) - RETURN this0 { .title } AS this" + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/fulltext/node-labels.test.ts b/packages/graphql/tests/tck/fulltext/node-labels.test.ts index 91d7a6eb8a..5571af2750 100644 --- a/packages/graphql/tests/tck/fulltext/node-labels.test.ts +++ b/packages/graphql/tests/tck/fulltext/node-labels.test.ts @@ -25,7 +25,7 @@ describe("Cypher -> fulltext -> Additional Labels", () => { test("simple match with single fulltext property and static additionalLabels", async () => { const typeDefs = /* GraphQL */ ` type Movie - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) @node(labels: ["Movie", "AnotherLabel"]) { title: String } @@ -37,8 +37,12 @@ describe("Cypher -> fulltext -> Additional Labels", () => { const query = /* GraphQL */ ` query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } + } } } `; @@ -46,9 +50,18 @@ describe("Cypher -> fulltext -> Additional Labels", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND $param2 IN labels(this0)) - RETURN this0 { .title } AS this" + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -63,7 +76,7 @@ describe("Cypher -> fulltext -> Additional Labels", () => { test("simple match with single fulltext property and jwt additionalLabels", async () => { const typeDefs = /* GraphQL */ ` type Movie - @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) @node(labels: ["Movie", "$jwt.label"]) { title: String } @@ -80,8 +93,12 @@ describe("Cypher -> fulltext -> Additional Labels", () => { const query = /* GraphQL */ ` query { - movies(fulltext: { MovieTitle: { phrase: "something AND something" } }) { - title + moviesByTitle(phrase: "something AND something") { + edges { + node { + title + } + } } } `; @@ -92,9 +109,18 @@ describe("Cypher -> fulltext -> Additional Labels", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND $param2 IN labels(this0)) - RETURN this0 { .title } AS this" + WITH collect({ node: this0 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" } }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/fulltext/score.test.ts b/packages/graphql/tests/tck/fulltext/score.test.ts index f334eb3bad..1f7e3b704b 100644 --- a/packages/graphql/tests/tck/fulltext/score.test.ts +++ b/packages/graphql/tests/tck/fulltext/score.test.ts @@ -26,7 +26,9 @@ describe("Cypher -> fulltext -> Score", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` - type Movie @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) @node { + type Movie + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @node { title: String released: Int } @@ -40,11 +42,13 @@ describe("Cypher -> fulltext -> Score", () => { test("simple match with single property and score", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle(phrase: "a different name") { - score - movie { - title - released + moviesByTitle(phrase: "a different name") { + edges { + score + node { + title + released + } } } } @@ -53,9 +57,18 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) - RETURN this0 { .title, .released } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + RETURN collect({ node: { title: this0.title, released: this0.released, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -69,11 +82,13 @@ describe("Cypher -> fulltext -> Score", () => { test("simple match with single property and score and filter", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle(phrase: "a different name", where: { movie: { released_GT: 2000 } }) { - score - movie { - title - released + moviesByTitle(phrase: "a different name", where: { node: { released: { gt: 2000 } } }) { + edges { + score + node { + title + released + } } } } @@ -82,9 +97,18 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND this0.released > $param2) - RETURN this0 { .title, .released } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + RETURN collect({ node: { title: this0.title, released: this0.released, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -102,10 +126,12 @@ describe("Cypher -> fulltext -> Score", () => { test("with score filtering", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle(phrase: "a different name", where: { score: { min: 0.5 } }) { - score - movie { - title + moviesByTitle(phrase: "a different name", where: { score: { min: 0.5 } }) { + edges { + score + node { + title + } } } } @@ -114,9 +140,18 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE ($param1 IN labels(this0) AND var1 >= $param2) - RETURN this0 { .title } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -131,10 +166,12 @@ describe("Cypher -> fulltext -> Score", () => { test("with sorting", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle(phrase: "a different name", sort: { movie: { title: DESC } }) { - score - movie { - title + moviesByTitle(phrase: "a different name", sort: { node: { title: DESC } }) { + edges { + score + node { + title + } } } } @@ -143,11 +180,20 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) - WITH * - ORDER BY this0.title DESC - RETURN this0 { .title } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + WITH * + ORDER BY this0.title DESC + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -161,10 +207,12 @@ describe("Cypher -> fulltext -> Score", () => { test("with score sorting", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle(phrase: "a different name", sort: { score: ASC }) { - score - movie { - title + moviesByTitle(phrase: "a different name", sort: { score: ASC }) { + edges { + score + node { + title + } } } } @@ -173,11 +221,20 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) - WITH * - ORDER BY var1 ASC - RETURN this0 { .title } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + WITH * + ORDER BY var1 ASC + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -191,13 +248,12 @@ describe("Cypher -> fulltext -> Score", () => { test("with score and normal sorting", async () => { const query = /* GraphQL */ ` query { - moviesFulltextMovieTitle( - phrase: "a different name" - sort: [{ score: ASC }, { movie: { title: DESC } }] - ) { - score - movie { - title + moviesByTitle(phrase: "a different name", sort: [{ score: ASC }, { node: { title: DESC } }]) { + edges { + score + node { + title + } } } } @@ -206,11 +262,20 @@ describe("Cypher -> fulltext -> Score", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 + "CYPHER 5 + CALL db.index.fulltext.queryNodes(\\"MovieTitle\\", $param0) YIELD node AS this0, score AS var1 WHERE $param1 IN labels(this0) - WITH * - ORDER BY var1 ASC, this0.title DESC - RETURN this0 { .title } AS movie, var1 AS score" + WITH collect({ node: this0, score: var1 }) AS edges + WITH edges, size(edges) AS totalCount + CALL { + WITH edges + UNWIND edges AS edge + WITH edge.node AS this0, edge.score AS var1 + WITH * + ORDER BY var1 ASC, this0.title DESC + RETURN collect({ node: { title: this0.title, __resolveType: \\"Movie\\" }, score: var1 }) AS var2 + } + RETURN { edges: var2, totalCount: totalCount } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/global-node.test.ts b/packages/graphql/tests/tck/global-node.test.ts index d89ce10e07..544d4f8a80 100644 --- a/packages/graphql/tests/tck/global-node.test.ts +++ b/packages/graphql/tests/tck/global-node.test.ts @@ -25,12 +25,12 @@ describe("Global nodes", () => { test("it should fetch the correct node and fields", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - name: ID! @id @unique @relayId + name: ID! @id @relayId movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } `; @@ -57,7 +57,8 @@ describe("Global nodes", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -65,12 +66,12 @@ describe("Global nodes", () => { test("it should project the correct node and fields when id is the idField", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Movie!]! @relationship(type: "ACTED_IN", direction: IN) } `; @@ -94,7 +95,8 @@ describe("Global nodes", () => { variableValues: { id: toGlobalId({ typeName: "Actor", field: "dbId", id: "123455" }) }, }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.id = $param0 RETURN this { .name, dbId: this.id } AS this" `); @@ -108,12 +110,12 @@ describe("Global nodes", () => { test("it should project the correct selectionSet when id is used as a where argument", async () => { const typeDefs = /* GraphQL */ ` type Actor @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! movies: [Actor!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node { - title: ID! @id @unique @relayId + title: ID! @id @relayId actors: [Movie!]! @relationship(type: "ACTED_IN", direction: IN) } `; @@ -138,7 +140,8 @@ describe("Global nodes", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.id = $param0 RETURN this { .name } AS this" `); @@ -180,7 +183,8 @@ describe("Global nodes", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.dbId = $param0 RETURN this { .dbId, .name } AS this" `); diff --git a/packages/graphql/tests/tck/info.test.ts b/packages/graphql/tests/tck/info.test.ts index 686010529d..f3706c3ae3 100644 --- a/packages/graphql/tests/tck/info.test.ts +++ b/packages/graphql/tests/tck/info.test.ts @@ -57,7 +57,8 @@ describe("info", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) diff --git a/packages/graphql/tests/tck/issues/1115.test.ts b/packages/graphql/tests/tck/issues/1115.test.ts deleted file mode 100644 index febad4d19e..0000000000 --- a/packages/graphql/tests/tck/issues/1115.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1115", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type Parent @node { - children: [Child!]! @relationship(type: "HAS", direction: IN) - } - - type Child @node { - tcId: String @unique - } - - extend type Child - @authorization( - validate: [ - { - operations: [READ, CREATE, UPDATE, DELETE, CREATE_RELATIONSHIP, DELETE_RELATIONSHIP] - where: { jwt: { roles_INCLUDES: "upstream" } } - } - { operations: [READ], where: { jwt: { roles_INCLUDES: "downstream" } } } - ] - ) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - test("multiple connectOrCreate operations with auth", async () => { - const query = /* GraphQL */ ` - mutation UpdateParents { - updateParents( - update: { - children: { - connectOrCreate: [ - { where: { node: { tcId_EQ: "123" } }, onCreate: { node: { tcId: "123" } } } - { where: { node: { tcId_EQ: "456" } }, onCreate: { node: { tcId: "456" } } } - ] - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - contextValues: { token: createBearerToken("secret") }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Parent) - WITH this - CALL { - WITH this - MERGE (this_children0_connectOrCreate0:Child { tcId: $this_children0_connectOrCreate_param0 }) - ON CREATE SET - this_children0_connectOrCreate0.tcId = $this_children0_connectOrCreate_param1 - MERGE (this)<-[this_children0_connectOrCreate_this0:HAS]-(this_children0_connectOrCreate0) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_children0_connectOrCreate_param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_children0_connectOrCreate1:Child { tcId: $this_children0_connectOrCreate_param5 }) - ON CREATE SET - this_children0_connectOrCreate1.tcId = $this_children0_connectOrCreate_param6 - MERGE (this)<-[this_children0_connectOrCreate_this1:HAS]-(this_children0_connectOrCreate1) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $this_children0_connectOrCreate_param7 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this_children0_connectOrCreate_param0\\": \\"123\\", - \\"this_children0_connectOrCreate_param1\\": \\"123\\", - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"this_children0_connectOrCreate_param4\\": \\"upstream\\", - \\"this_children0_connectOrCreate_param5\\": \\"456\\", - \\"this_children0_connectOrCreate_param6\\": \\"456\\", - \\"this_children0_connectOrCreate_param7\\": \\"upstream\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1131.test.ts b/packages/graphql/tests/tck/issues/1131.test.ts deleted file mode 100644 index 3e71d237b6..0000000000 --- a/packages/graphql/tests/tck/issues/1131.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1131", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type BibliographicReference @node(labels: ["BibliographicReference", "Resource"]) { - iri: ID! @unique @alias(property: "uri") - prefLabel: [String] - isInPublication: [Concept!]! @relationship(type: "isInPublication", direction: OUT) - } - - type Concept @node(labels: ["Concept", "Resource"]) { - iri: ID! @unique @alias(property: "uri") - prefLabel: [String]! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("where with multiple filters and params", async () => { - const query = /* GraphQL */ ` - mutation { - updateBibliographicReferences( - where: { iri_EQ: "urn:myiri2" } - update: { - prefLabel_SET: "Updated Label:My BRS with Resource" - isInPublication: [ - { - connectOrCreate: { - where: { node: { iri_EQ: "new-g" } } - onCreate: { node: { iri: "new-g", prefLabel: "pub" } } - } - } - { - connectOrCreate: { - where: { node: { iri_EQ: "new-f" } } - onCreate: { node: { iri: "new-f", prefLabel: "pub" } } - } - } - ] - } - ) { - bibliographicReferences { - iri - prefLabel - isInPublication(where: { iri_IN: ["new-f", "new-e"] }) { - iri - prefLabel - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:BibliographicReference:Resource) - WHERE this.uri = $param0 - SET this.prefLabel = $this_update_prefLabel_SET - WITH this - CALL { - WITH this - MERGE (this_isInPublication0_connectOrCreate0:Concept:Resource { uri: $this_isInPublication0_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication0_connectOrCreate0.uri = $this_isInPublication0_connectOrCreate_param1, - this_isInPublication0_connectOrCreate0.prefLabel = $this_isInPublication0_connectOrCreate_param2 - MERGE (this)-[this_isInPublication0_connectOrCreate_this0:isInPublication]->(this_isInPublication0_connectOrCreate0) - RETURN count(*) AS _ - } - WITH this - CALL { - WITH this - MERGE (this_isInPublication1_connectOrCreate0:Concept:Resource { uri: $this_isInPublication1_connectOrCreate_param0 }) - ON CREATE SET - this_isInPublication1_connectOrCreate0.uri = $this_isInPublication1_connectOrCreate_param1, - this_isInPublication1_connectOrCreate0.prefLabel = $this_isInPublication1_connectOrCreate_param2 - MERGE (this)-[this_isInPublication1_connectOrCreate_this0:isInPublication]->(this_isInPublication1_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:isInPublication]->(update_this1:Concept:Resource) - WHERE update_this1.uri IN $update_param0 - WITH update_this1 { .prefLabel, iri: update_this1.uri } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .prefLabel, iri: this.uri, isInPublication: update_var2 }) AS data" - `); - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"update_param0\\": [ - \\"new-f\\", - \\"new-e\\" - ], - \\"param0\\": \\"urn:myiri2\\", - \\"this_update_prefLabel_SET\\": [ - \\"Updated Label:My BRS with Resource\\" - ], - \\"this_isInPublication0_connectOrCreate_param0\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param1\\": \\"new-g\\", - \\"this_isInPublication0_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"this_isInPublication1_connectOrCreate_param0\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param1\\": \\"new-f\\", - \\"this_isInPublication1_connectOrCreate_param2\\": [ - \\"pub\\" - ], - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1132.test.ts b/packages/graphql/tests/tck/issues/1132.test.ts index d2a2743aca..0194429794 100644 --- a/packages/graphql/tests/tck/issues/1132.test.ts +++ b/packages/graphql/tests/tck/issues/1132.test.ts @@ -28,7 +28,11 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { @node @authorization( validate: [ - { when: [BEFORE], operations: [CREATE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } } + { + when: [BEFORE] + operations: [CREATE_RELATIONSHIP] + where: { node: { id: { eq: "$jwt.sub" } } } + } ] ) { id: ID! @@ -47,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { const query = /* GraphQL */ ` mutation { - updateSources(update: { targets: { connect: { where: { node: { id_EQ: 1 } } } } }) { + updateSources(update: { targets: { connect: { where: { node: { id: { eq: 1 } } } } } }) { sources { id } @@ -61,7 +65,8 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Source) + "CYPHER 5 + MATCH (this:Source) WITH * CALL { WITH this @@ -74,7 +79,7 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_targets0_connect0_node - MERGE (this)-[:HAS_TARGET]->(this_targets0_connect0_node) + CREATE (this)-[:HAS_TARGET]->(this_targets0_connect0_node) } } WITH this, this_targets0_connect0_node @@ -102,7 +107,11 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { @node @authorization( validate: [ - { when: [BEFORE], operations: [DELETE_RELATIONSHIP], where: { node: { id_EQ: "$jwt.sub" } } } + { + when: [BEFORE] + operations: [DELETE_RELATIONSHIP] + where: { node: { id: { eq: "$jwt.sub" } } } + } ] ) { id: ID! @@ -121,7 +130,7 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { const query = /* GraphQL */ ` mutation { - updateSources(update: { targets: { disconnect: { where: { node: { id_EQ: 1 } } } } }) { + updateSources(update: { targets: { disconnect: { where: { node: { id: { eq: 1 } } } } } }) { sources { id } @@ -135,7 +144,8 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Source) + "CYPHER 5 + MATCH (this:Source) WITH this CALL { WITH this @@ -169,7 +179,9 @@ describe("https://github.com/neo4j/graphql/issues/1132", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"1\\" + \\"id\\": { + \\"eq\\": \\"1\\" + } } } } diff --git a/packages/graphql/tests/tck/issues/1150.test.ts b/packages/graphql/tests/tck/issues/1150.test.ts index 111dce734f..2337c228e7 100644 --- a/packages/graphql/tests/tck/issues/1150.test.ts +++ b/packages/graphql/tests/tck/issues/1150.test.ts @@ -31,19 +31,19 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { } type Battery @node { - id: ID! @unique + id: ID! current: Boolean! } - extend type Battery @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "admin" } } }]) + extend type Battery @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }]) type CombustionEngine @node { - id: ID! @unique + id: ID! current: Boolean! } type Drive @node { - id: ID! @unique + id: ID! current: Boolean! driveCompositions: [DriveComposition!]! @relationship(type: "CONSISTS_OF", properties: "RelationProps", direction: OUT) @@ -52,7 +52,7 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { union DriveComponent = Battery | CombustionEngine type DriveComposition @node { - id: ID! @unique + id: ID! current: Boolean! driveComponent: [DriveComponent!]! @relationship(type: "HAS", properties: "RelationProps", direction: OUT) @@ -70,15 +70,15 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { const query = /* GraphQL */ ` query getDrivesWithFilteredUnionType { - drives(where: { current_EQ: true }) { + drives(where: { current: { eq: true } }) { current - driveCompositionsConnection(where: { edge: { current_EQ: true } }) { + driveCompositionsConnection(where: { edge: { current: { eq: true } } }) { edges { node { driveComponentConnection( where: { - Battery: { edge: { current_EQ: true } } - CombustionEngine: { edge: { current_EQ: true } } + Battery: { edge: { current: { eq: true } } } + CombustionEngine: { edge: { current: { eq: true } } } } ) { edges { @@ -108,7 +108,8 @@ describe("https://github.com/neo4j/graphql/issues/1150", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Drive) + "CYPHER 5 + MATCH (this:Drive) WHERE this.current = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/issues/1182.test.ts b/packages/graphql/tests/tck/issues/1182.test.ts deleted file mode 100644 index 33330e8a4c..0000000000 --- a/packages/graphql/tests/tck/issues/1182.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1182", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Movie @node { - id: ID! @id @unique - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) - } - - type Actor @node { - id: ID! @id @unique - name: String! - dob: DateTime! - homeAddress: Point! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("DateTime and Point values get set as expected", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { - title: "Forrest Gump" - actors: { - connectOrCreate: { - where: { node: { id_EQ: 1 } } - onCreate: { - node: { - name: "Tom Hanks" - dob: "1970-01-01T00:00:00.000Z" - homeAddress: { longitude: 1, latitude: 2 } - } - } - } - } - } - ] - ) { - movies { - title - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = randomUUID() - SET this0.title = $this0_title - WITH this0 - CALL { - WITH this0 - MERGE (this0_actors_connectOrCreate0:Actor { id: $this0_actors_connectOrCreate_param0 }) - ON CREATE SET - this0_actors_connectOrCreate0.name = $this0_actors_connectOrCreate_param1, - this0_actors_connectOrCreate0.dob = $this0_actors_connectOrCreate_param2, - this0_actors_connectOrCreate0.homeAddress = $this0_actors_connectOrCreate_param3 - MERGE (this0)<-[this0_actors_connectOrCreate_this0:ACTED_IN]-(this0_actors_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Forrest Gump\\", - \\"this0_actors_connectOrCreate_param0\\": \\"1\\", - \\"this0_actors_connectOrCreate_param1\\": \\"Tom Hanks\\", - \\"this0_actors_connectOrCreate_param2\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"this0_actors_connectOrCreate_param3\\": { - \\"longitude\\": 1, - \\"latitude\\": 2 - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1221.test.ts b/packages/graphql/tests/tck/issues/1221.test.ts deleted file mode 100644 index f563874f0c..0000000000 --- a/packages/graphql/tests/tck/issues/1221.test.ts +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1221", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("should apply where filter for deep relations, two relations deep", async () => { - typeDefs = /* GraphQL */ ` - type Series @node { - id: ID! @unique - current: Boolean! - architecture: [MasterData!]! - @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) - } - - type NameDetails @mutation(operations: []) @query(read: false, aggregate: false) @node { - fullName: String! - } - - type RelationProps @relationshipProperties { - current: Boolean! - } - - type MasterData @node { - id: ID! @unique - current: Boolean! - nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - - const query = /* GraphQL */ ` - query { - series( - where: { - current_EQ: true - architectureConnection_SINGLE: { - node: { nameDetailsConnection: { node: { fullName_EQ: "MHA" } } } - } - } - ) { - id - architectureConnection(where: { edge: { current_EQ: true } }) { - edges { - node { - nameDetailsConnection(where: { edge: { current_EQ: true } }) { - edges { - node { - fullName - } - } - } - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Series) - WHERE (this.current = $param0 AND single(this1 IN [(this)-[this3:ARCHITECTURE]->(this1:MasterData) WHERE single(this0 IN [(this1)-[this2:HAS_NAME]->(this0:NameDetails) WHERE this0.fullName = $param1 | 1] WHERE true) | 1] WHERE true)) - CALL { - WITH this - MATCH (this)-[this4:ARCHITECTURE]->(this5:MasterData) - WHERE this4.current = $param2 - WITH collect({ node: this5, relationship: this4 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this5, edge.relationship AS this4 - CALL { - WITH this5 - MATCH (this5)-[this6:HAS_NAME]->(this7:NameDetails) - WHERE this6.current = $param3 - WITH collect({ node: this7, relationship: this6 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this7, edge.relationship AS this6 - RETURN collect({ node: { fullName: this7.fullName, __resolveType: \\"NameDetails\\" } }) AS var8 - } - RETURN { edges: var8, totalCount: totalCount } AS var9 - } - RETURN collect({ node: { nameDetailsConnection: var9, __resolveType: \\"MasterData\\" } }) AS var10 - } - RETURN { edges: var10, totalCount: totalCount } AS var11 - } - RETURN this { .id, architectureConnection: var11 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": true, - \\"param1\\": \\"MHA\\", - \\"param2\\": true, - \\"param3\\": true - }" - `); - }); - - test("should apply where filter for deep relations, three relations deep", async () => { - typeDefs = /* GraphQL */ ` - type Main @node { - id: ID! @unique - current: Boolean! - main: [Series!]! @relationship(type: "MAIN", properties: "RelationProps", direction: OUT) - } - - type Series @node { - id: ID! @unique - current: Boolean! - architecture: [MasterData!]! - @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) - } - - type NameDetails @mutation(operations: []) @query(read: false, aggregate: false) @node { - fullName: String! - } - - type RelationProps @relationshipProperties { - current: Boolean! - } - - type MasterData @node { - id: ID! @unique - current: Boolean! - nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - - const query = /* GraphQL */ ` - query { - mains( - where: { - current_EQ: true - mainConnection_SINGLE: { - node: { - architectureConnection_SOME: { - node: { nameDetailsConnection: { node: { fullName_EQ: "MHA" } } } - } - } - } - } - ) { - id - mainConnection(where: { edge: { current_EQ: true } }) { - edges { - node { - architectureConnection(where: { edge: { current_EQ: true } }) { - edges { - node { - nameDetailsConnection(where: { edge: { current_EQ: true } }) { - edges { - node { - fullName - } - } - } - } - } - } - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Main) - WHERE (this.current = $param0 AND single(this0 IN [(this)-[this5:MAIN]->(this0:Series) WHERE EXISTS { - MATCH (this0)-[this1:ARCHITECTURE]->(this2:MasterData) - WHERE single(this3 IN [(this2)-[this4:HAS_NAME]->(this3:NameDetails) WHERE this3.fullName = $param1 | 1] WHERE true) - } | 1] WHERE true)) - CALL { - WITH this - MATCH (this)-[this6:MAIN]->(this7:Series) - WHERE this6.current = $param2 - WITH collect({ node: this7, relationship: this6 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this7, edge.relationship AS this6 - CALL { - WITH this7 - MATCH (this7)-[this8:ARCHITECTURE]->(this9:MasterData) - WHERE this8.current = $param3 - WITH collect({ node: this9, relationship: this8 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this9, edge.relationship AS this8 - CALL { - WITH this9 - MATCH (this9)-[this10:HAS_NAME]->(this11:NameDetails) - WHERE this10.current = $param4 - WITH collect({ node: this11, relationship: this10 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this11, edge.relationship AS this10 - RETURN collect({ node: { fullName: this11.fullName, __resolveType: \\"NameDetails\\" } }) AS var12 - } - RETURN { edges: var12, totalCount: totalCount } AS var13 - } - RETURN collect({ node: { nameDetailsConnection: var13, __resolveType: \\"MasterData\\" } }) AS var14 - } - RETURN { edges: var14, totalCount: totalCount } AS var15 - } - RETURN collect({ node: { architectureConnection: var15, __resolveType: \\"Series\\" } }) AS var16 - } - RETURN { edges: var16, totalCount: totalCount } AS var17 - } - RETURN this { .id, mainConnection: var17 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": true, - \\"param1\\": \\"MHA\\", - \\"param2\\": true, - \\"param3\\": true, - \\"param4\\": true - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1249.test.ts b/packages/graphql/tests/tck/issues/1249.test.ts index f95a30cb34..c46554fbd4 100644 --- a/packages/graphql/tests/tck/issues/1249.test.ts +++ b/packages/graphql/tests/tck/issues/1249.test.ts @@ -29,7 +29,7 @@ describe("https://github.com/neo4j/graphql/issues/1249", () => { type Bulk @mutation(operations: []) @node(labels: ["Bulk", "$tenant"]) { id: ID! supplierMaterialNumber: String! - material: Material! @relationship(type: "MATERIAL_BULK", direction: OUT) + material: [Material!]! @relationship(type: "MATERIAL_BULK", direction: OUT) } type Material @mutation(operations: []) @node { @@ -81,10 +81,12 @@ describe("https://github.com/neo4j/graphql/issues/1249", () => { const result = await translateQuery(neoSchema, query, { contextValues: { cypherParams: { tenant: "BULK" } } }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Bulk:BULK) + "CYPHER 5 + MATCH (this:Bulk:BULK) CALL { WITH this MATCH (this)-[this0:MATERIAL_BULK]->(this1:Material) + WITH DISTINCT this1 CALL { WITH this1 MATCH (this1)-[this2:MATERIAL_SUPPLIER]->(this3:Supplier) @@ -99,7 +101,7 @@ describe("https://github.com/neo4j/graphql/issues/1249", () => { RETURN { edges: var4, totalCount: totalCount } AS var5 } WITH this1 { .id, suppliersConnection: var5 } AS this1 - RETURN head(collect(this1)) AS var6 + RETURN collect(this1) AS var6 } RETURN this { .supplierMaterialNumber, material: var6 } AS this" `); diff --git a/packages/graphql/tests/tck/issues/1320.test.ts b/packages/graphql/tests/tck/issues/1320.test.ts index 15ad4e6b01..bfe0f83647 100644 --- a/packages/graphql/tests/tck/issues/1320.test.ts +++ b/packages/graphql/tests/tck/issues/1320.test.ts @@ -28,8 +28,8 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { typeDefs = /* GraphQL */ ` type Risk @node { code: String! - ownedBy: Team @relationship(type: "OWNS_RISK", direction: IN) - mitigationState: [MitigationState] + ownedBy: [Team!]! @relationship(type: "OWNS_RISK", direction: IN) + mitigationState: [MitigationState!] } type Team @node { @@ -54,10 +54,10 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { const query = /* GraphQL */ ` query getAggreationOnTeams { stats: teams { - accepted: ownsRisksAggregate(where: { mitigationState_INCLUDES: Accepted }) { + accepted: ownsRisksAggregate(where: { mitigationState: { includes: Accepted } }) { count } - identified: ownsRisksAggregate(where: { mitigationState_INCLUDES: Identified }) { + identified: ownsRisksAggregate(where: { mitigationState: { includes: Identified } }) { count } } @@ -66,7 +66,8 @@ describe("https://github.com/neo4j/graphql/issues/1320", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Team) + "CYPHER 5 + MATCH (this:Team) CALL { WITH this MATCH (this)-[this0:OWNS_RISK]->(this1:Risk) diff --git a/packages/graphql/tests/tck/issues/1348.test.ts b/packages/graphql/tests/tck/issues/1348.test.ts index 4eb85b9017..35d393c6c2 100644 --- a/packages/graphql/tests/tck/issues/1348.test.ts +++ b/packages/graphql/tests/tck/issues/1348.test.ts @@ -33,16 +33,14 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { type Series implements Product @node { productTitle: String! - releatsTo: [Product!]! - @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + releatsTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) seasons: [Season!]! } type Season implements Product @node { productTitle: String! - releatsTo: [Product!]! - @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + releatsTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) seasonNumber: Int episodes: [ProgrammeItem!]! @@ -50,8 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { type ProgrammeItem implements Product @node { productTitle: String! - releatsTo: [Product!]! - @relationship(type: "RELATES_TO", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + releatsTo: [Product!]! @relationship(type: "RELATES_TO", direction: OUT, queryDirection: UNDIRECTED) episodeNumber: Int } @@ -79,7 +76,8 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ProgrammeItem) + "CYPHER 5 + MATCH (this:ProgrammeItem) CALL { WITH this CALL { @@ -135,7 +133,8 @@ describe("https://github.com/neo4j/graphql/issues/1348", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ProgrammeItem) + "CYPHER 5 + MATCH (this:ProgrammeItem) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/1364.test.ts b/packages/graphql/tests/tck/issues/1364.test.ts index c40b9b8233..86568c4803 100644 --- a/packages/graphql/tests/tck/issues/1364.test.ts +++ b/packages/graphql/tests/tck/issues/1364.test.ts @@ -91,7 +91,8 @@ describe("https://github.com/neo4j/graphql/issues/1364", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -134,7 +135,8 @@ describe("https://github.com/neo4j/graphql/issues/1364", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -178,7 +180,8 @@ describe("https://github.com/neo4j/graphql/issues/1364", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { diff --git a/packages/graphql/tests/tck/issues/1430.test.ts b/packages/graphql/tests/tck/issues/1430.test.ts deleted file mode 100644 index e574e3d01f..0000000000 --- a/packages/graphql/tests/tck/issues/1430.test.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1430", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type ABCE @node { - id: ID @id @unique - name: String - interface: InterfaceMom @relationship(type: "HAS_INTERFACE", direction: OUT) - } - - interface InterfaceMom { - id: ID - name: String - } - - type ChildOne implements InterfaceMom @node { - id: ID @id @unique - name: String - feathur: String - } - - type ChildTwo implements InterfaceMom @node { - id: ID @id @unique - name: String - sth: String - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should not allow to create more than one node for a one-to-one relationship", async () => { - const query = /* GraphQL */ ` - mutation ddfs { - updateAbces( - where: { id_EQ: "TestID" } - update: { interface: { create: { node: { ChildOne: { name: "childone name2" } } } } } - ) { - abces { - id - interface { - id - name - __typename - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ABCE) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)-[:HAS_INTERFACE]->(:ChildOne)) OR EXISTS((this)-[:HAS_INTERFACE]->(:ChildTwo)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"ABCE\\",\\"interface\\"]) - CREATE (this_interface0_create0_node:ChildOne) - SET this_interface0_create0_node.id = randomUUID() - SET this_interface0_create0_node.name = $this_interface0_create0_node_name - MERGE (this)-[:HAS_INTERFACE]->(this_interface0_create0_node) - RETURN count(*) AS update_this_ChildOne - } - CALL { - WITH this - WITH this - RETURN count(*) AS update_this_ChildTwo - } - WITH * - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[update_this0:HAS_INTERFACE]->(update_this1:ChildOne) - WITH update_this1 { .id, .name, __resolveType: \\"ChildOne\\", __id: id(update_this1) } AS update_this1 - RETURN update_this1 AS update_var2 - UNION - WITH * - MATCH (this)-[update_this3:HAS_INTERFACE]->(update_this4:ChildTwo) - WITH update_this4 { .id, .name, __resolveType: \\"ChildTwo\\", __id: id(update_this4) } AS update_this4 - RETURN update_this4 AS update_var2 - } - WITH update_var2 - RETURN head(collect(update_var2)) AS update_var2 - } - RETURN collect(DISTINCT this { .id, interface: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"TestID\\", - \\"this_interface0_create0_node_name\\": \\"childone name2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should not allow connecting a second node to an existing one-to-one relationship", async () => { - const query = /* GraphQL */ ` - mutation { - updateAbces( - where: { id: "TestId" } - update: { interface: { connect: { where: { node: { name: "childone name connect" } } } } } - ) { - abces { - id - interface { - id - name - __typename - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ABCE) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)-[:HAS_INTERFACE]->(:ChildOne)) OR EXISTS((this)-[:HAS_INTERFACE]->(:ChildTwo)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"ABCE\\",\\"interface\\"]) - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_interface0_connect0_node:ChildOne) - WHERE this_interface0_connect0_node.name = $this_interface0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_interface0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_interface0_connect0_node - MERGE (this)-[:HAS_INTERFACE]->(this_interface0_connect0_node) - } - } - WITH this, this_interface0_connect0_node - RETURN count(*) AS connect_this_interface0_connect_ChildOne0 - } - RETURN count(*) AS update_this_ChildOne - } - CALL { - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)-[:HAS_INTERFACE]->(:ChildOne)) OR EXISTS((this)-[:HAS_INTERFACE]->(:ChildTwo)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"ABCE\\",\\"interface\\"]) - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_interface0_connect0_node:ChildTwo) - WHERE this_interface0_connect0_node.name = $this_interface0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_interface0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_interface0_connect0_node - MERGE (this)-[:HAS_INTERFACE]->(this_interface0_connect0_node) - } - } - WITH this, this_interface0_connect0_node - RETURN count(*) AS connect_this_interface0_connect_ChildTwo0 - } - RETURN count(*) AS update_this_ChildTwo - } - WITH * - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[update_this0:HAS_INTERFACE]->(update_this1:ChildOne) - WITH update_this1 { .id, .name, __resolveType: \\"ChildOne\\", __id: id(update_this1) } AS update_this1 - RETURN update_this1 AS update_var2 - UNION - WITH * - MATCH (this)-[update_this3:HAS_INTERFACE]->(update_this4:ChildTwo) - WITH update_this4 { .id, .name, __resolveType: \\"ChildTwo\\", __id: id(update_this4) } AS update_this4 - RETURN update_this4 AS update_var2 - } - WITH update_var2 - RETURN head(collect(update_var2)) AS update_var2 - } - RETURN collect(DISTINCT this { .id, interface: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"TestId\\", - \\"this_interface0_connect0_node_param0\\": \\"childone name connect\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1528.test.ts b/packages/graphql/tests/tck/issues/1528.test.ts index 6c88ffa442..28d847f95a 100644 --- a/packages/graphql/tests/tck/issues/1528.test.ts +++ b/packages/graphql/tests/tck/issues/1528.test.ts @@ -69,7 +69,8 @@ describe("https://github.com/neo4j/graphql/issues/1528", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Genre) + "CYPHER 5 + MATCH (this:Genre) CALL { WITH this MATCH (this)<-[this0:IS_GENRE]-(this1:Movie) diff --git a/packages/graphql/tests/tck/issues/1535.test.ts b/packages/graphql/tests/tck/issues/1535.test.ts index bf798273e6..c6263f8496 100644 --- a/packages/graphql/tests/tck/issues/1535.test.ts +++ b/packages/graphql/tests/tck/issues/1535.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Tenant @node { - id: ID! @id @unique + id: ID! @id name: String! events: [Event!]! @relationship(type: "HOSTED_BY", direction: IN) fooBars: [FooBar!]! @relationship(type: "HAS_FOOBARS", direction: OUT) @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type Screening implements Event @node { - id: ID! @id @unique + id: ID! @id title: String beginsAt: DateTime! } @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { } type FooBar @node { - id: ID! @id @unique + id: ID! @id name: String! } `; @@ -78,7 +78,8 @@ describe("https://github.com/neo4j/graphql/issues/1535", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tenant) + "CYPHER 5 + MATCH (this:Tenant) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/1536.test.ts b/packages/graphql/tests/tck/issues/1536.test.ts deleted file mode 100644 index 3f101b3193..0000000000 --- a/packages/graphql/tests/tck/issues/1536.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1536", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type SomeNode @node { - id: ID! @id @unique - other: OtherNode! @relationship(type: "HAS_OTHER_NODES", direction: OUT) - } - - type OtherNode @node { - id: ID! @id @unique - interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT) - } - - interface MyInterface { - id: ID! - } - - type MyImplementation implements MyInterface @node { - id: ID! @id @unique - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("query nested interfaces", async () => { - const query = /* GraphQL */ ` - query { - someNodes { - id - other { - interfaceField { - id - } - } - } - } - `; - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:SomeNode) - CALL { - WITH this - MATCH (this)-[this0:HAS_OTHER_NODES]->(this1:OtherNode) - CALL { - WITH this1 - CALL { - WITH * - MATCH (this1)-[this2:HAS_INTERFACE_NODES]->(this3:MyImplementation) - WITH this3 { .id, __resolveType: \\"MyImplementation\\", __id: id(this3) } AS this3 - RETURN this3 AS var4 - } - WITH var4 - RETURN head(collect(var4)) AS var4 - } - WITH this1 { interfaceField: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { .id, other: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1566.test.ts b/packages/graphql/tests/tck/issues/1566.test.ts index d257793b85..6ddf24ed21 100644 --- a/packages/graphql/tests/tck/issues/1566.test.ts +++ b/packages/graphql/tests/tck/issues/1566.test.ts @@ -61,7 +61,7 @@ describe("https://github.com/neo4j/graphql/issues/1566", () => { test("collect unions returned by cypher directive", async () => { const query = /* GraphQL */ ` query { - communities(where: { id_EQ: 4656564 }) { + communities(where: { id: { eq: 4656564 } }) { id hasFeedItems { __typename @@ -79,7 +79,8 @@ describe("https://github.com/neo4j/graphql/issues/1566", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Community) + "CYPHER 5 + MATCH (this:Community) WHERE this.id = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/issues/1628.test.ts b/packages/graphql/tests/tck/issues/1628.test.ts index fbf86e23a2..8944f31aed 100644 --- a/packages/graphql/tests/tck/issues/1628.test.ts +++ b/packages/graphql/tests/tck/issues/1628.test.ts @@ -30,7 +30,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { """ IRI """ - iri: ID! @unique @alias(property: "uri") + iri: ID! @alias(property: "uri") dcterms__title: [dcterms_title!]! @relationship(type: "dcterms__title", direction: OUT) } @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { test("Filter generated by query doesn't utilise index", async () => { const query = /* GraphQL */ ` { - frbrWorks(limit: 10000, where: { dcterms__title_SOME: { value_CONTAINS: "0777" } }) { + frbrWorks(limit: 10000, where: { dcterms__title: { some: { value_CONTAINS: "0777" } } }) { iri dcterms__title(where: { value_CONTAINS: "0777" }) { value @@ -59,7 +59,8 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:frbr__Work:Resource) + "CYPHER 5 + MATCH (this:frbr__Work:Resource) WHERE EXISTS { MATCH (this)-[:dcterms__title]->(this0:dcterms_title:property) WHERE this0.value CONTAINS $param0 @@ -70,6 +71,7 @@ describe("https://github.com/neo4j/graphql/issues/1628", () => { WITH this MATCH (this)-[this1:dcterms__title]->(this2:dcterms_title:property) WHERE this2.value CONTAINS $param2 + WITH DISTINCT this2 WITH this2 { .value } AS this2 RETURN collect(this2) AS var3 } diff --git a/packages/graphql/tests/tck/issues/1687.test.ts b/packages/graphql/tests/tck/issues/1687.test.ts index f27f7d1f90..56d91e2d19 100644 --- a/packages/graphql/tests/tck/issues/1687.test.ts +++ b/packages/graphql/tests/tck/issues/1687.test.ts @@ -52,7 +52,7 @@ describe("https://github.com/neo4j/graphql/issues/1687", () => { test("should be able to return all the genres related to the Matrix movie using connection fields", async () => { const query = /* GraphQL */ ` query Genres { - genres(where: { moviesConnection_ALL: { node: { title_EQ: "Matrix" } } }) { + genres(where: { moviesConnection: { all: { node: { title: { eq: "Matrix" } } } } }) { name } } @@ -61,7 +61,8 @@ describe("https://github.com/neo4j/graphql/issues/1687", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Genre) + "CYPHER 5 + MATCH (this:Genre) WHERE (EXISTS { MATCH (this)<-[this0:HAS_GENRE]-(this1) WHERE (this1.title = $param0 AND this1:Movie) diff --git a/packages/graphql/tests/tck/issues/1751.test.ts b/packages/graphql/tests/tck/issues/1751.test.ts index 5dd86c736a..4c188b3609 100644 --- a/packages/graphql/tests/tck/issues/1751.test.ts +++ b/packages/graphql/tests/tck/issues/1751.test.ts @@ -27,14 +27,14 @@ describe("https://github.com/neo4j/graphql/issues/1751", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Organization @node { - organizationId: ID! @id @unique + organizationId: ID! @id title: String createdAt: DateTime! admins: [Admin!]! @relationship(type: "HAS_ADMINISTRATOR", direction: OUT) } type Admin @node { - adminId: ID! @id @unique + adminId: ID! @id createdAt: DateTime! isSuperAdmin: Boolean organizations: [Organization!]! @relationship(type: "HAS_ADMINISTRATOR", direction: IN) @@ -58,7 +58,7 @@ describe("https://github.com/neo4j/graphql/issues/1751", () => { const variableValues = { where: { - title_EQ: "Google", + title: { eq: "Google" }, }, delete: { admins: [ @@ -66,7 +66,7 @@ describe("https://github.com/neo4j/graphql/issues/1751", () => { where: { node: { organizationsAggregate: { - count_EQ: 1, + count: { eq: 1 }, }, }, }, @@ -78,7 +78,8 @@ describe("https://github.com/neo4j/graphql/issues/1751", () => { const result = await translateQuery(neoSchema, query, { variableValues }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Organization) + "CYPHER 5 + MATCH (this:Organization) WHERE this.title = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/issues/1756.test.ts b/packages/graphql/tests/tck/issues/1756.test.ts deleted file mode 100644 index 1f58eeb5b5..0000000000 --- a/packages/graphql/tests/tck/issues/1756.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1756", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - interface INode { - id: ID - } - - type Product implements INode @node { - id: ID @populatedBy(operations: [CREATE], callback: "nanoid") - name: String! - genre: [Genre!]! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Genre implements INode @node { - id: ID @populatedBy(operations: [CREATE], callback: "nanoid") - value: String! @unique - } - `; - - const nanoid = () => { - return `callback_value`; - }; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { populatedBy: { callbacks: { nanoid } } }, - }); - }); - - test("should define the ID using the callback function", async () => { - const query = /* GraphQL */ ` - mutation { - createProducts( - input: { - name: "TestProduct" - genre: { - connectOrCreate: [ - { where: { node: { value_EQ: "Action" } }, onCreate: { node: { value: "Action" } } } - ] - } - } - ) { - products { - id - name - genre { - id - value - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Product) - SET this0.id = $resolvedCallbacks.this0_id_nanoid - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_genre_connectOrCreate0:Genre { value: $this0_genre_connectOrCreate_param0 }) - ON CREATE SET - this0_genre_connectOrCreate0.value = $this0_genre_connectOrCreate_param1, - this0_genre_connectOrCreate0.id = $resolvedCallbacks.this0_genre_connectOrCreate0_id_nanoid - MERGE (this0)-[this0_genre_connectOrCreate_this0:HAS_GENRE]->(this0_genre_connectOrCreate0) - RETURN count(*) AS _ - } - RETURN this0 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_GENRE]->(create_this1:Genre) - WITH create_this1 { .id, .value } AS create_this1 - RETURN collect(create_this1) AS create_var2 - } - RETURN this0 { .id, .name, genre: create_var2 } AS create_var3 - } - RETURN [create_var3] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"TestProduct\\", - \\"this0_genre_connectOrCreate_param0\\": \\"Action\\", - \\"this0_genre_connectOrCreate_param1\\": \\"Action\\", - \\"resolvedCallbacks\\": { - \\"this0_id_nanoid\\": \\"callback_value\\", - \\"this0_genre_connectOrCreate0_id_nanoid\\": \\"callback_value\\" - } - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1760.test.ts b/packages/graphql/tests/tck/issues/1760.test.ts deleted file mode 100644 index 45a08d1ea1..0000000000 --- a/packages/graphql/tests/tck/issues/1760.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/1760", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - interface BusinessObject { - id: ID! - nameDetails: NameDetails - } - - type ApplicationVariant implements BusinessObject - @node - @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) - @mutation(operations: []) { - markets: [Market!]! @relationship(type: "HAS_MARKETS", direction: OUT) - id: ID! @unique - relatedId: ID - @cypher(statement: "MATCH (this)<-[:HAS_BASE]-(n:BaseObject) RETURN n.id as res", columnName: "res") - baseObject: BaseObject! @relationship(type: "HAS_BASE", direction: IN) - current: Boolean! - nameDetails: NameDetails @relationship(type: "HAS_NAME", direction: OUT) - } - - type NameDetails - @node - @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) - @mutation(operations: []) - @query(read: false, aggregate: false) { - fullName: String! - } - - type Market implements BusinessObject - @node - @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) - @mutation(operations: []) { - id: ID! @unique - nameDetails: NameDetails @relationship(type: "HAS_NAME", direction: OUT) - } - - type BaseObject - @node - @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "ALL" } } }]) - @mutation(operations: []) { - id: ID! @id @unique - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - test("Cypher fields should be calculated early in query if needed for sort, sort applied after initial match", async () => { - const query = /* GraphQL */ ` - query getApplicationVariants( - $where: ApplicationVariantWhere - $limit: Int - $offset: Int - $sort: [ApplicationVariantSort!] - ) { - applicationVariants(where: $where, limit: $limit, offset: $offset, sort: $sort) { - relatedId - nameDetailsConnection { - edges { - node { - fullName - } - } - } - marketsConnection { - edges { - node { - nameDetailsConnection { - edges { - node { - fullName - } - } - } - } - } - } - baseObjectConnection { - edges { - node { - id - } - } - } - } - } - `; - - const variableValues = { - where: { - current_EQ: true, - }, - - sort: { - relatedId: "ASC", - }, - offset: 0, - limit: 50, - }; - - const result = await translateQuery(neoSchema, query, { - variableValues, - contextValues: { token: createBearerToken("secret") }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:ApplicationVariant) - WITH * - WHERE (this.current = $param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - CALL { - WITH this - CALL { - WITH this - WITH this AS this - MATCH (this)<-[:HAS_BASE]-(n:BaseObject) RETURN n.id as res - } - WITH res AS this0 - RETURN this0 AS var1 - } - WITH * - ORDER BY var1 ASC - SKIP $param4 - LIMIT $param5 - CALL { - WITH this - MATCH (this)-[this2:HAS_NAME]->(this3:NameDetails) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this3, relationship: this2 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this3, edge.relationship AS this2 - RETURN collect({ node: { fullName: this3.fullName, __resolveType: \\"NameDetails\\" } }) AS var4 - } - RETURN { edges: var4, totalCount: totalCount } AS var5 - } - CALL { - WITH this - MATCH (this)-[this6:HAS_MARKETS]->(this7:Market) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param7 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this7, relationship: this6 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this7, edge.relationship AS this6 - CALL { - WITH this7 - MATCH (this7)-[this8:HAS_NAME]->(this9:NameDetails) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param8 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this9, relationship: this8 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this9, edge.relationship AS this8 - RETURN collect({ node: { fullName: this9.fullName, __resolveType: \\"NameDetails\\" } }) AS var10 - } - RETURN { edges: var10, totalCount: totalCount } AS var11 - } - RETURN collect({ node: { nameDetailsConnection: var11, __resolveType: \\"Market\\" } }) AS var12 - } - RETURN { edges: var12, totalCount: totalCount } AS var13 - } - CALL { - WITH this - MATCH (this)<-[this14:HAS_BASE]-(this15:BaseObject) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param9 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this15, relationship: this14 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this15, edge.relationship AS this14 - RETURN collect({ node: { id: this15.id, __resolveType: \\"BaseObject\\" } }) AS var16 - } - RETURN { edges: var16, totalCount: totalCount } AS var17 - } - RETURN this { relatedId: var1, nameDetailsConnection: var5, marketsConnection: var13, baseObjectConnection: var17 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": true, - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"param3\\": \\"ALL\\", - \\"param4\\": { - \\"low\\": 0, - \\"high\\": 0 - }, - \\"param5\\": { - \\"low\\": 50, - \\"high\\": 0 - }, - \\"param6\\": \\"ALL\\", - \\"param7\\": \\"ALL\\", - \\"param8\\": \\"ALL\\", - \\"param9\\": \\"ALL\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/1779.test.ts b/packages/graphql/tests/tck/issues/1779.test.ts index cadf72fd0b..7d4906e581 100644 --- a/packages/graphql/tests/tck/issues/1779.test.ts +++ b/packages/graphql/tests/tck/issues/1779.test.ts @@ -48,7 +48,7 @@ describe("https://github.com/neo4j/graphql/issues/1779", () => { { people { name - attends(where: { students_ALL: { age_GT: 23 } }) { + attends(where: { students: { all: { age: { gt: 23 } } } }) { name } } @@ -58,7 +58,8 @@ describe("https://github.com/neo4j/graphql/issues/1779", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) + "CYPHER 5 + MATCH (this:Person) CALL { WITH this MATCH (this)-[this0:attends]->(this1:School) @@ -69,6 +70,7 @@ describe("https://github.com/neo4j/graphql/issues/1779", () => { MATCH (this1)<-[:attends]-(this2:Person) WHERE NOT (this2.age > $param0) })) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var3 } diff --git a/packages/graphql/tests/tck/issues/1783.test.ts b/packages/graphql/tests/tck/issues/1783.test.ts index 0b37de1ceb..fa9132f8c6 100644 --- a/packages/graphql/tests/tck/issues/1783.test.ts +++ b/packages/graphql/tests/tck/issues/1783.test.ts @@ -27,11 +27,12 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Series @node { - id: ID! @unique + id: ID! current: Boolean! architecture: [MasterData!]! @relationship(type: "ARCHITECTURE", properties: "RelationProps", direction: OUT) - nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [NameDetails!]! + @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } type NameDetails @mutation(operations: []) @query(read: false, aggregate: false) @node { @@ -43,9 +44,10 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { } type MasterData @node { - id: ID! @unique + id: ID! current: Boolean! - nameDetails: NameDetails @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) + nameDetails: [NameDetails!]! + @relationship(type: "HAS_NAME", properties: "RelationProps", direction: OUT) } `; @@ -85,41 +87,54 @@ describe("https://github.com/neo4j/graphql/issues/1783", () => { const variableValues = { where: { - current_EQ: true, + current: { eq: true }, nameDetailsConnection: { - edge: { - current_EQ: true, - }, - node: { - fullName_CONTAINS: "1", + some: { + edge: { + current: { eq: true }, + }, + node: { + fullName_CONTAINS: "1", + }, }, }, - architectureConnection_SINGLE: { - edge: { - current_EQ: true, - }, - node: { - nameDetailsConnection: { - edge: { - current_EQ: true, - }, - node: { - fullName_EQ: "MHA", + architectureConnection: { + single: { + edge: { + current: { eq: true }, + }, + node: { + nameDetailsConnection: { + some: { + edge: { + current: { eq: true }, + }, + node: { + fullName: { eq: "MHA" }, + }, + }, }, }, }, }, }, connectionWhere: { - current_EQ: true, + current: { eq: true }, }, }; const result = await translateQuery(neoSchema, query, { variableValues }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Series) - WHERE (this.current = $param0 AND single(this2 IN [(this)-[this3:ARCHITECTURE]->(this2:MasterData) WHERE (single(this0 IN [(this2)-[this1:HAS_NAME]->(this0:NameDetails) WHERE (this0.fullName = $param1 AND this1.current = $param2) | 1] WHERE true) AND this3.current = $param3) | 1] WHERE true) AND single(this4 IN [(this)-[this5:HAS_NAME]->(this4:NameDetails) WHERE (this4.fullName CONTAINS $param4 AND this5.current = $param5) | 1] WHERE true)) + "CYPHER 5 + MATCH (this:Series) + WHERE (this.current = $param0 AND single(this0 IN [(this)-[this3:ARCHITECTURE]->(this0:MasterData) WHERE (EXISTS { + MATCH (this0)-[this1:HAS_NAME]->(this2:NameDetails) + WHERE (this2.fullName = $param1 AND this1.current = $param2) + } AND this3.current = $param3) | 1] WHERE true) AND EXISTS { + MATCH (this)-[this4:HAS_NAME]->(this5:NameDetails) + WHERE (this5.fullName CONTAINS $param4 AND this4.current = $param5) + }) CALL { WITH this MATCH (this)-[this6:HAS_NAME]->(this7:NameDetails) diff --git a/packages/graphql/tests/tck/issues/1848.test.ts b/packages/graphql/tests/tck/issues/1848.test.ts index 6523ae6cd5..4056dcf308 100644 --- a/packages/graphql/tests/tck/issues/1848.test.ts +++ b/packages/graphql/tests/tck/issues/1848.test.ts @@ -27,17 +27,17 @@ describe("https://github.com/neo4j/graphql/issues/1848", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type ContentPiece @node(labels: ["ContentPiece", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type Project @node(labels: ["Project", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int } type Community @node(labels: ["Community", "UNIVERSAL"]) { - uid: String! @unique + uid: String! id: Int hasContentPieces: [ContentPiece!]! @relationship(type: "COMMUNITY_CONTENTPIECE_HASCONTENTPIECES", direction: OUT) @@ -86,7 +86,8 @@ describe("https://github.com/neo4j/graphql/issues/1848", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Community:UNIVERSAL) + "CYPHER 5 + MATCH (this:Community:UNIVERSAL) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/190.test.ts b/packages/graphql/tests/tck/issues/190.test.ts index ea1b10d370..0d45fd3479 100644 --- a/packages/graphql/tests/tck/issues/190.test.ts +++ b/packages/graphql/tests/tck/issues/190.test.ts @@ -48,7 +48,7 @@ describe("#190", () => { test("Example 1", async () => { const query = /* GraphQL */ ` query { - users(where: { demographics_SOME: { type_EQ: "Gender", value_EQ: "Female" } }) { + users(where: { demographics: { some: { type: { eq: "Gender" }, value: { eq: "Female" } } } }) { uid demographics { type @@ -61,7 +61,8 @@ describe("#190", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:HAS_DEMOGRAPHIC]->(this0:UserDemographics) WHERE (this0.type = $param0 AND this0.value = $param1) @@ -69,6 +70,7 @@ describe("#190", () => { CALL { WITH this MATCH (this)-[this1:HAS_DEMOGRAPHIC]->(this2:UserDemographics) + WITH DISTINCT this2 WITH this2 { .type, .value } AS this2 RETURN collect(this2) AS var3 } @@ -88,8 +90,14 @@ describe("#190", () => { query { users( where: { - demographics_SOME: { - OR: [{ type_EQ: "Gender", value_EQ: "Female" }, { type_EQ: "State" }, { type_EQ: "Age" }] + demographics: { + some: { + OR: [ + { type: { eq: "Gender" }, value: { eq: "Female" } } + { type: { eq: "State" } } + { type: { eq: "Age" } } + ] + } } } ) { @@ -105,7 +113,8 @@ describe("#190", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:HAS_DEMOGRAPHIC]->(this0:UserDemographics) WHERE ((this0.type = $param0 AND this0.value = $param1) OR this0.type = $param2 OR this0.type = $param3) @@ -113,6 +122,7 @@ describe("#190", () => { CALL { WITH this MATCH (this)-[this1:HAS_DEMOGRAPHIC]->(this2:UserDemographics) + WITH DISTINCT this2 WITH this2 { .type, .value } AS this2 RETURN collect(this2) AS var3 } diff --git a/packages/graphql/tests/tck/issues/1933.test.ts b/packages/graphql/tests/tck/issues/1933.test.ts index b47941853f..9b0c041363 100644 --- a/packages/graphql/tests/tck/issues/1933.test.ts +++ b/packages/graphql/tests/tck/issues/1933.test.ts @@ -27,7 +27,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Employee @node { - employeeId: ID! @unique + employeeId: ID! firstName: String! @settable(onCreate: false, onUpdate: false) lastName: String @settable(onCreate: false, onUpdate: false) projects: [Project!]! @@ -39,7 +39,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { } type Project @node { - projectId: ID! @unique + projectId: ID! name: String! @settable(onCreate: false, onUpdate: false) description: String employees: [Employee!]! @@ -55,7 +55,7 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { test("should compare for SUM_LTE allocation in return statement rather than the WITH clause", async () => { const query = /* GraphQL */ ` { - employees(where: { projectsAggregate: { edge: { allocation_SUM_LTE: 25 } } }) { + employees(where: { projectsAggregate: { edge: { allocation: { sum: { lte: 25 } } } } }) { employeeId firstName lastName @@ -77,7 +77,8 @@ describe("https://github.com/neo4j/graphql/issues/1933", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Employee) + "CYPHER 5 + MATCH (this:Employee) CALL { WITH this MATCH (this)-[this0:PARTICIPATES]->(this1:Project) diff --git a/packages/graphql/tests/tck/issues/2022.test.ts b/packages/graphql/tests/tck/issues/2022.test.ts index 618b2bbb55..9053935e59 100644 --- a/packages/graphql/tests/tck/issues/2022.test.ts +++ b/packages/graphql/tests/tck/issues/2022.test.ts @@ -27,24 +27,24 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type ArtPiece @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") title: String! - auction: AuctionItem! @relationship(type: "SOLD_AT_AUCTION_AS", direction: OUT) - owner: Organization! @relationship(type: "OWNED_BY", direction: OUT) + auction: [AuctionItem!]! @relationship(type: "SOLD_AT_AUCTION_AS", direction: OUT) + owner: [Organization!]! @relationship(type: "OWNED_BY", direction: OUT) } type AuctionItem @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") auctionName: String! lotNumber: Int! - item: ArtPiece! @relationship(type: "SOLD_AT_AUCTION_AS", direction: IN) - buyer: Organization! @relationship(type: "BOUGHT_ITEM_AT_AUCTION", direction: IN) - seller: Organization! @relationship(type: "SOLD_ITEM_AT_AUCTION", direction: IN) + item: [ArtPiece!]! @relationship(type: "SOLD_AT_AUCTION_AS", direction: IN) + buyer: [Organization!]! @relationship(type: "BOUGHT_ITEM_AT_AUCTION", direction: IN) + seller: [Organization!]! @relationship(type: "SOLD_ITEM_AT_AUCTION", direction: IN) } type Organization @node { - dbId: ID! @id @unique @relayId @alias(property: "id") + dbId: ID! @id @relayId @alias(property: "id") name: String! artCollection: [ArtPiece!]! @relationship(type: "OWNED_BY", direction: IN) @@ -88,7 +88,8 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:ArtPiece) + "CYPHER 5 + MATCH (this0:ArtPiece) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -98,20 +99,23 @@ describe("https://github.com/neo4j/graphql/issues/2022", () => { CALL { WITH this0 MATCH (this0)-[this1:SOLD_AT_AUCTION_AS]->(this2:AuctionItem) + WITH DISTINCT this2 CALL { WITH this2 MATCH (this2)<-[this3:BOUGHT_ITEM_AT_AUCTION]-(this4:Organization) + WITH DISTINCT this4 WITH this4 { .name, dbId: this4.id } AS this4 - RETURN head(collect(this4)) AS var5 + RETURN collect(this4) AS var5 } WITH this2 { .auctionName, .lotNumber, dbId: this2.id, buyer: var5 } AS this2 - RETURN head(collect(this2)) AS var6 + RETURN collect(this2) AS var6 } CALL { WITH this0 MATCH (this0)-[this7:OWNED_BY]->(this8:Organization) + WITH DISTINCT this8 WITH this8 { .name, dbId: this8.id } AS this8 - RETURN head(collect(this8)) AS var9 + RETURN collect(this8) AS var9 } RETURN collect({ node: { dbId: this0.id, title: this0.title, auction: var6, owner: var9, __resolveType: \\"ArtPiece\\" } }) AS var10 } diff --git a/packages/graphql/tests/tck/issues/2100.test.ts b/packages/graphql/tests/tck/issues/2100.test.ts index d9cc8d04d9..f9632b7f08 100644 --- a/packages/graphql/tests/tck/issues/2100.test.ts +++ b/packages/graphql/tests/tck/issues/2100.test.ts @@ -41,7 +41,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { """ columnName: "markedAttendance" ) - serviceDate: TimeGraph! @relationship(type: "BUSSED_ON", direction: OUT) + serviceDate: [TimeGraph!]! @relationship(type: "BUSSED_ON", direction: OUT) } interface Church { @@ -51,7 +51,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { } type Bacenta implements Church @node { - id: ID @id @unique + id: ID @id name: String! serviceLogs: [ServiceLog!]! @relationship(type: "HAS_HISTORY", direction: OUT) bussing(limit: Int!): [BussingRecord!]! @@ -73,7 +73,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { id: ID! attendance: Int markedAttendance: Boolean! - serviceDate: TimeGraph! @declareRelationship + serviceDate: [TimeGraph!]! @declareRelationship } `; @@ -85,7 +85,7 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { test("query nested relations under a root connection field", async () => { const query = /* GraphQL */ ` query { - bacentas(where: { id_EQ: 1 }) { + bacentas(where: { id: { eq: 1 } }) { id name bussing(limit: 10) { @@ -105,7 +105,8 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Bacenta) + "CYPHER 5 + MATCH (this:Bacenta) WHERE this.id = $param0 CALL { WITH this @@ -120,8 +121,9 @@ describe("https://github.com/neo4j/graphql/issues/2100", () => { CALL { WITH this0 MATCH (this0)-[this1:BUSSED_ON]->(this2:TimeGraph) + WITH DISTINCT this2 WITH this2 { .date } AS this2 - RETURN head(collect(this2)) AS var3 + RETURN collect(this2) AS var3 } CALL { WITH this0 diff --git a/packages/graphql/tests/tck/issues/2189.test.ts b/packages/graphql/tests/tck/issues/2189.test.ts index 0ff354e024..e4d3d1a59f 100644 --- a/packages/graphql/tests/tck/issues/2189.test.ts +++ b/packages/graphql/tests/tck/issues/2189.test.ts @@ -24,31 +24,31 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { let neoSchema: Neo4jGraphQL; beforeEach(() => { - const typeDefs = ` + const typeDefs = /* GraphQL */ ` type Test_Item @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean - feedback: Test_Feedback @relationship(type: "TEST_RELATIONSHIP", direction: IN) + feedback: [Test_Feedback!]! @relationship(type: "TEST_RELATIONSHIP", direction: IN) feedbackCypher: Test_Feedback @cypher( statement: """ OPTIONAL MATCH (this)<-[:TEST_RELATIONSHIP]-(t:Test_Feedback) RETURN t LIMIT 1 - """, + """ columnName: "t" ) } type Test_Feedback @node { - uuid: ID! @id @unique + uuid: ID! @id int: Int str: String bool: Boolean - item: Test_Item @relationship(type: "TEST_RELATIONSHIP", direction: OUT) + item: [Test_Item!]! @relationship(type: "TEST_RELATIONSHIP", direction: OUT) } `; @@ -76,7 +76,8 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Test_Item) @@ -94,23 +95,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { create_this3.uuid = randomUUID(), create_this3.str = create_var2.node.str MERGE (create_this1)<-[create_this4:TEST_RELATIONSHIP]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:TEST_RELATIONSHIP]->(:Test_Item) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Feedback.item must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:TEST_RELATIONSHIP]-(:Test_Feedback) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Item.feedback must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } @@ -127,11 +112,13 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { \\"str\\": \\"one\\", \\"bool\\": false, \\"feedback\\": { - \\"create\\": { - \\"node\\": { - \\"str\\": \\"hi there\\" + \\"create\\": [ + { + \\"node\\": { + \\"str\\": \\"hi there\\" + } } - } + ] } }, { @@ -166,7 +153,8 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Test_Item) @@ -184,23 +172,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { create_this3.uuid = randomUUID(), create_this3.str = create_var2.node.str MERGE (create_this1)<-[create_this4:TEST_RELATIONSHIP]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:TEST_RELATIONSHIP]->(:Test_Item) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Feedback.item must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:TEST_RELATIONSHIP]-(:Test_Feedback) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Item.feedback must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } @@ -217,11 +189,13 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { \\"str\\": \\"one\\", \\"bool\\": false, \\"feedback\\": { - \\"create\\": { - \\"node\\": { - \\"str\\": \\"hi there\\" + \\"create\\": [ + { + \\"node\\": { + \\"str\\": \\"hi there\\" + } } - } + ] } }, { @@ -274,7 +248,8 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Test_Item) @@ -292,23 +267,7 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { create_this3.uuid = randomUUID(), create_this3.str = create_var2.node.str MERGE (create_this1)<-[create_this4:TEST_RELATIONSHIP]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:TEST_RELATIONSHIP]->(:Test_Item) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Feedback.item must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:TEST_RELATIONSHIP]-(:Test_Feedback) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Item.feedback must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } @@ -321,17 +280,18 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { RETURN t LIMIT 1 } - WITH t AS create_this10 - WITH create_this10 { .bool, .str, .int, .uuid } AS create_this10 - RETURN head(collect(create_this10)) AS create_var11 + WITH t AS create_this6 + WITH create_this6 { .bool, .str, .int, .uuid } AS create_this6 + RETURN head(collect(create_this6)) AS create_var7 } CALL { WITH create_this1 - MATCH (create_this1)<-[create_this12:TEST_RELATIONSHIP]-(create_this13:Test_Feedback) - WITH create_this13 { .uuid, .int, .str, .bool } AS create_this13 - RETURN head(collect(create_this13)) AS create_var14 + MATCH (create_this1)<-[create_this8:TEST_RELATIONSHIP]-(create_this9:Test_Feedback) + WITH DISTINCT create_this9 + WITH create_this9 { .uuid, .int, .str, .bool } AS create_this9 + RETURN collect(create_this9) AS create_var10 } - RETURN collect(create_this1 { .bool, .int, .str, .uuid, feedbackCypher: create_var11, feedback: create_var14 }) AS data" + RETURN collect(create_this1 { .bool, .int, .str, .uuid, feedbackCypher: create_var7, feedback: create_var10 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -344,11 +304,13 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { \\"str\\": \\"one\\", \\"bool\\": false, \\"feedback\\": { - \\"create\\": { - \\"node\\": { - \\"str\\": \\"hi there\\" + \\"create\\": [ + { + \\"node\\": { + \\"str\\": \\"hi there\\" + } } - } + ] } }, { @@ -395,7 +357,8 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Test_Item) @@ -413,33 +376,18 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { create_this3.uuid = randomUUID(), create_this3.str = create_var2.node.str MERGE (create_this1)<-[create_this4:TEST_RELATIONSHIP]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:TEST_RELATIONSHIP]->(:Test_Item) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Feedback.item must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:TEST_RELATIONSHIP]-(:Test_Feedback) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDTest_Item.feedback must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } CALL { WITH create_this1 - MATCH (create_this1)<-[create_this10:TEST_RELATIONSHIP]-(create_this11:Test_Feedback) - WITH create_this11 { .uuid, .int, .str, .bool } AS create_this11 - RETURN head(collect(create_this11)) AS create_var12 + MATCH (create_this1)<-[create_this6:TEST_RELATIONSHIP]-(create_this7:Test_Feedback) + WITH DISTINCT create_this7 + WITH create_this7 { .uuid, .int, .str, .bool } AS create_this7 + RETURN collect(create_this7) AS create_var8 } - RETURN collect(create_this1 { .bool, .int, .str, .uuid, feedback: create_var12 }) AS data" + RETURN collect(create_this1 { .bool, .int, .str, .uuid, feedback: create_var8 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -452,11 +400,13 @@ describe("https://github.com/neo4j/graphql/issues/2189", () => { \\"str\\": \\"one\\", \\"bool\\": false, \\"feedback\\": { - \\"create\\": { - \\"node\\": { - \\"str\\": \\"hi there\\" + \\"create\\": [ + { + \\"node\\": { + \\"str\\": \\"hi there\\" + } } - } + ] } }, { diff --git a/packages/graphql/tests/tck/issues/2249.test.ts b/packages/graphql/tests/tck/issues/2249.test.ts index a110cb03a4..fa29327dd9 100644 --- a/packages/graphql/tests/tck/issues/2249.test.ts +++ b/packages/graphql/tests/tck/issues/2249.test.ts @@ -38,8 +38,8 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { type Person implements Reviewer @node { name: String! reputation: Int! - id: Int @unique - reviewerId: Int @unique + id: Int + reviewerId: Int } type Influencer implements Reviewer @node { @@ -63,7 +63,7 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { const query = /* GraphQL */ ` mutation UpdateMovies { updateMovies( - where: { title_EQ: "John Wick" } + where: { title: { eq: "John Wick" } } update: { reviewers: [ { create: [{ edge: { score: 10 }, node: { Person: { reputation: 100, name: "Ana" } } }] } @@ -85,7 +85,8 @@ describe("https://github.com/neo4j/graphql/issues/2249", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/2262.test.ts b/packages/graphql/tests/tck/issues/2262.test.ts index ae67e79f92..ecf9ef29b3 100644 --- a/packages/graphql/tests/tck/issues/2262.test.ts +++ b/packages/graphql/tests/tck/issues/2262.test.ts @@ -28,7 +28,7 @@ describe("https://github.com/neo4j/graphql/issues/2262", () => { typeDefs = /* GraphQL */ ` type Component @node { uuid: String - upstreamProcess: Process @relationship(type: "OUTPUT", direction: IN) + upstreamProcess: [Process!]! @relationship(type: "OUTPUT", direction: IN) downstreamProcesses: [Process!]! @relationship(type: "INPUT", direction: OUT) } @@ -47,7 +47,7 @@ describe("https://github.com/neo4j/graphql/issues/2262", () => { test("query nested relations under a root connection field", async () => { const query = /* GraphQL */ ` query ComponentsProcesses { - components(where: { uuid_EQ: "c1" }) { + components(where: { uuid: { eq: "c1" } }) { uuid upstreamProcessConnection { edges { @@ -69,7 +69,8 @@ describe("https://github.com/neo4j/graphql/issues/2262", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Component) + "CYPHER 5 + MATCH (this:Component) WHERE this.uuid = $param0 CALL { WITH this diff --git a/packages/graphql/tests/tck/issues/2267.test.ts b/packages/graphql/tests/tck/issues/2267.test.ts index 91b8cb65e8..65956f8640 100644 --- a/packages/graphql/tests/tck/issues/2267.test.ts +++ b/packages/graphql/tests/tck/issues/2267.test.ts @@ -67,7 +67,8 @@ describe("https://github.com/neo4j/graphql/issues/2267", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Place) + "CYPHER 5 + MATCH (this:Place) WITH * ORDER BY this.displayName ASC CALL { diff --git a/packages/graphql/tests/tck/issues/2396.test.ts b/packages/graphql/tests/tck/issues/2396.test.ts index 748319c3d7..65e02022f2 100644 --- a/packages/graphql/tests/tck/issues/2396.test.ts +++ b/packages/graphql/tests/tck/issues/2396.test.ts @@ -29,25 +29,25 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { typeDefs = /* GraphQL */ ` type PostalCode @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! address: [Address!]! @relationship(type: "HAS_POSTAL_CODE", direction: IN) } - extend type PostalCode @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type PostalCode @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) union AddressNode = Estate type Address @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - address: String! @unique + address: String! streetNumber: String route: String! @coalesce(value: "") - postalCode: PostalCode! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) + postalCode: [PostalCode!]! @relationship(type: "HAS_POSTAL_CODE", direction: OUT) locality: String! @coalesce(value: "") administrativeAreaLevel1: String! @coalesce(value: "") administrativeAreaLevel2: String @@ -57,31 +57,31 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { node: [AddressNode!]! @relationship(type: "HAS_ADDRESS", direction: IN) } - extend type Address @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type Address @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) type Mandate @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - number: String! @unique + number: String! createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) price: Float! - valuation: Valuation! @relationship(type: "HAS_VALUATION", direction: OUT) + valuation: [Valuation!]! @relationship(type: "HAS_VALUATION", direction: OUT) } - extend type Mandate @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type Mandate @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) type Valuation @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - estate: Estate @relationship(type: "VALUATION_FOR", direction: OUT) + estate: [Estate!]! @relationship(type: "VALUATION_FOR", direction: OUT) } - extend type Valuation @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type Valuation @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) enum EstateType { APARTMENT @@ -100,7 +100,7 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { type Estate @mutation(operations: [CREATE, UPDATE]) @node { archivedAt: DateTime - uuid: ID! @id @unique + uuid: ID! @id createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -108,10 +108,10 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { area: Float! floor: Int - address: Address @relationship(type: "HAS_ADDRESS", direction: OUT) + address: [Address!]! @relationship(type: "HAS_ADDRESS", direction: OUT) } - extend type Estate @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type Estate @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) `; neoSchema = new Neo4jGraphQL({ @@ -139,8 +139,12 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { offset: null, where: { valuation: { - estate: { - floor_GTE: 0, + some: { + estate: { + some: { + floor: { gte: 0 }, + }, + }, }, }, }, @@ -152,28 +156,35 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Mandate) - OPTIONAL MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Mandate) WITH * - WHERE ((var1 <> 0 AND single(this2 IN [(this0)-[:VALUATION_FOR]->(this2:Estate) WHERE this2.floor >= $param0 | 1] WHERE true)) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) + WHERE (EXISTS { + MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) + WHERE EXISTS { + MATCH (this0)-[:VALUATION_FOR]->(this1:Estate) + WHERE this1.floor >= $param0 + } + } AND ($isAuthenticated = true AND this.archivedAt IS NULL)) CALL { WITH this - MATCH (this)-[this3:HAS_VALUATION]->(this4:Valuation) + MATCH (this)-[this2:HAS_VALUATION]->(this3:Valuation) + WITH DISTINCT this3 WITH * - WHERE ($isAuthenticated = true AND this4.archivedAt IS NULL) + WHERE ($isAuthenticated = true AND this3.archivedAt IS NULL) CALL { - WITH this4 - MATCH (this4)-[this5:VALUATION_FOR]->(this6:Estate) + WITH this3 + MATCH (this3)-[this4:VALUATION_FOR]->(this5:Estate) + WITH DISTINCT this5 WITH * - WHERE ($isAuthenticated = true AND this6.archivedAt IS NULL) - WITH this6 { .uuid } AS this6 - RETURN head(collect(this6)) AS var7 + WHERE ($isAuthenticated = true AND this5.archivedAt IS NULL) + WITH this5 { .uuid } AS this5 + RETURN collect(this5) AS var6 } - WITH this4 { estate: var7 } AS this4 - RETURN head(collect(this4)) AS var8 + WITH this3 { estate: var6 } AS this3 + RETURN collect(this3) AS var7 } - RETURN this { valuation: var8 } AS this" + RETURN this { valuation: var7 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -205,10 +216,14 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: null, offset: null, where: { - price_GTE: 0, + price: { gte: 0 }, valuation: { - estate: { - floor_GTE: 0, + some: { + estate: { + some: { + floor: { gte: 0 }, + }, + }, }, }, }, @@ -220,28 +235,35 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Mandate) - OPTIONAL MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) - WITH *, count(this0) AS var1 + "CYPHER 5 + MATCH (this:Mandate) WITH * - WHERE ((this.price >= $param0 AND (var1 <> 0 AND single(this2 IN [(this0)-[:VALUATION_FOR]->(this2:Estate) WHERE this2.floor >= $param1 | 1] WHERE true))) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) + WHERE ((this.price >= $param0 AND EXISTS { + MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) + WHERE EXISTS { + MATCH (this0)-[:VALUATION_FOR]->(this1:Estate) + WHERE this1.floor >= $param1 + } + }) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) CALL { WITH this - MATCH (this)-[this3:HAS_VALUATION]->(this4:Valuation) + MATCH (this)-[this2:HAS_VALUATION]->(this3:Valuation) + WITH DISTINCT this3 WITH * - WHERE ($isAuthenticated = true AND this4.archivedAt IS NULL) + WHERE ($isAuthenticated = true AND this3.archivedAt IS NULL) CALL { - WITH this4 - MATCH (this4)-[this5:VALUATION_FOR]->(this6:Estate) + WITH this3 + MATCH (this3)-[this4:VALUATION_FOR]->(this5:Estate) + WITH DISTINCT this5 WITH * - WHERE ($isAuthenticated = true AND this6.archivedAt IS NULL) - WITH this6 { .uuid } AS this6 - RETURN head(collect(this6)) AS var7 + WHERE ($isAuthenticated = true AND this5.archivedAt IS NULL) + WITH this5 { .uuid } AS this5 + RETURN collect(this5) AS var6 } - WITH this4 { estate: var7 } AS this4 - RETURN head(collect(this4)) AS var8 + WITH this3 { estate: var6 } AS this3 + RETURN collect(this3) AS var7 } - RETURN this { valuation: var8 } AS this" + RETURN this { valuation: var7 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -274,17 +296,25 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: null, offset: null, where: { - price_GTE: 0, + price: { gte: 0 }, valuation: { - estate: { - address: { - postalCode: { - number_IN: ["13001"], + some: { + estate: { + some: { + address: { + some: { + postalCode: { + some: { + number: { in: ["13001"] }, + }, + }, + }, + }, + area: { gte: 0 }, + estateType: { in: ["APARTMENT"] }, + floor: { gte: 0 }, }, }, - area_GTE: 0, - estateType_IN: ["APARTMENT"], - floor_GTE: 0, }, }, }, @@ -296,56 +326,46 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Mandate) - CALL { - WITH this + "CYPHER 5 + MATCH (this:Mandate) + WITH * + WHERE ((this.price >= $param0 AND EXISTS { MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) - CALL { - WITH this0 + WHERE EXISTS { MATCH (this0)-[:VALUATION_FOR]->(this1:Estate) - CALL { - WITH this1 + WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND EXISTS { MATCH (this1)-[:HAS_ADDRESS]->(this2:Address) - OPTIONAL MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) - WITH *, count(this3) AS var4 - WITH * - WHERE (var4 <> 0 AND this3.number IN $param0) - RETURN count(this2) = 1 AS var5 - } - WITH * - WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND var5 = true) - RETURN count(this1) = 1 AS var6 + WHERE EXISTS { + MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) + WHERE this3.number IN $param4 + } + }) } - WITH * - WHERE var6 = true - RETURN count(this0) = 1 AS var7 - } - WITH * - WHERE ((this.price >= $param4 AND var7 = true) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) + }) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) CALL { WITH this - MATCH (this)-[this8:HAS_VALUATION]->(this9:Valuation) + MATCH (this)-[this4:HAS_VALUATION]->(this5:Valuation) + WITH DISTINCT this5 WITH * - WHERE ($isAuthenticated = true AND this9.archivedAt IS NULL) + WHERE ($isAuthenticated = true AND this5.archivedAt IS NULL) CALL { - WITH this9 - MATCH (this9)-[this10:VALUATION_FOR]->(this11:Estate) + WITH this5 + MATCH (this5)-[this6:VALUATION_FOR]->(this7:Estate) + WITH DISTINCT this7 WITH * - WHERE ($isAuthenticated = true AND this11.archivedAt IS NULL) - WITH this11 { .uuid } AS this11 - RETURN head(collect(this11)) AS var12 + WHERE ($isAuthenticated = true AND this7.archivedAt IS NULL) + WITH this7 { .uuid } AS this7 + RETURN collect(this7) AS var8 } - WITH this9 { estate: var12 } AS this9 - RETURN head(collect(this9)) AS var13 + WITH this5 { estate: var8 } AS this5 + RETURN collect(this5) AS var9 } - RETURN this { valuation: var13 } AS this" + RETURN this { valuation: var9 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": [ - \\"13001\\" - ], + \\"param0\\": 0, \\"param1\\": [ \\"APARTMENT\\" ], @@ -354,7 +374,9 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { \\"low\\": 0, \\"high\\": 0 }, - \\"param4\\": 0, + \\"param4\\": [ + \\"13001\\" + ], \\"isAuthenticated\\": true }" `); @@ -378,17 +400,25 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: 20, sort: null, where: { - price_GTE: 0, + price: { gte: 0 }, valuation: { - estate: { - address: { - postalCode: { - number_IN: ["13001"], + some: { + estate: { + some: { + address: { + some: { + postalCode: { + some: { + number: { in: ["13001"] }, + }, + }, + }, + }, + area: { gte: 0 }, + estateType: { in: ["APARTMENT"] }, + floor: { gte: 0 }, }, }, - area_GTE: 0, - estateType_IN: ["APARTMENT"], - floor_GTE: 0, }, }, }, @@ -400,59 +430,49 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Mandate) - CALL { - WITH this + "CYPHER 5 + MATCH (this:Mandate) + WITH * + WHERE ((this.price >= $param0 AND EXISTS { MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) - CALL { - WITH this0 + WHERE EXISTS { MATCH (this0)-[:VALUATION_FOR]->(this1:Estate) - CALL { - WITH this1 + WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND EXISTS { MATCH (this1)-[:HAS_ADDRESS]->(this2:Address) - OPTIONAL MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) - WITH *, count(this3) AS var4 - WITH * - WHERE (var4 <> 0 AND this3.number IN $param0) - RETURN count(this2) = 1 AS var5 - } - WITH * - WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND var5 = true) - RETURN count(this1) = 1 AS var6 + WHERE EXISTS { + MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) + WHERE this3.number IN $param4 + } + }) } - WITH * - WHERE var6 = true - RETURN count(this0) = 1 AS var7 - } - WITH * - WHERE ((this.price >= $param4 AND var7 = true) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) + }) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) WITH * SKIP $param6 LIMIT $param7 CALL { WITH this - MATCH (this)-[this8:HAS_VALUATION]->(this9:Valuation) + MATCH (this)-[this4:HAS_VALUATION]->(this5:Valuation) + WITH DISTINCT this5 WITH * - WHERE ($isAuthenticated = true AND this9.archivedAt IS NULL) + WHERE ($isAuthenticated = true AND this5.archivedAt IS NULL) CALL { - WITH this9 - MATCH (this9)-[this10:VALUATION_FOR]->(this11:Estate) + WITH this5 + MATCH (this5)-[this6:VALUATION_FOR]->(this7:Estate) + WITH DISTINCT this7 WITH * - WHERE ($isAuthenticated = true AND this11.archivedAt IS NULL) - WITH this11 { .uuid } AS this11 - RETURN head(collect(this11)) AS var12 + WHERE ($isAuthenticated = true AND this7.archivedAt IS NULL) + WITH this7 { .uuid } AS this7 + RETURN collect(this7) AS var8 } - WITH this9 { estate: var12 } AS this9 - RETURN head(collect(this9)) AS var13 + WITH this5 { estate: var8 } AS this5 + RETURN collect(this5) AS var9 } - RETURN this { valuation: var13 } AS this" + RETURN this { valuation: var9 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": [ - \\"13001\\" - ], + \\"param0\\": 0, \\"param1\\": [ \\"APARTMENT\\" ], @@ -461,7 +481,9 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { \\"low\\": 0, \\"high\\": 0 }, - \\"param4\\": 0, + \\"param4\\": [ + \\"13001\\" + ], \\"isAuthenticated\\": true, \\"param6\\": { \\"low\\": 0, @@ -493,17 +515,25 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { limit: 40, sort: null, where: { - price_GTE: 0, + price: { gte: 0 }, valuation: { - estate: { - address: { - postalCode: { - number_IN: ["13001"], + some: { + estate: { + some: { + address: { + some: { + postalCode: { + some: { + number: { in: ["13001"] }, + }, + }, + }, + }, + area: { gte: 0 }, + estateType: { in: ["APARTMENT"] }, + floor: { gte: 0 }, }, }, - area_GTE: 0, - estateType_IN: ["APARTMENT"], - floor_GTE: 0, }, }, }, @@ -515,59 +545,49 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Mandate) - CALL { - WITH this + "CYPHER 5 + MATCH (this:Mandate) + WITH * + WHERE ((this.price >= $param0 AND EXISTS { MATCH (this)-[:HAS_VALUATION]->(this0:Valuation) - CALL { - WITH this0 + WHERE EXISTS { MATCH (this0)-[:VALUATION_FOR]->(this1:Estate) - CALL { - WITH this1 + WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND EXISTS { MATCH (this1)-[:HAS_ADDRESS]->(this2:Address) - OPTIONAL MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) - WITH *, count(this3) AS var4 - WITH * - WHERE (var4 <> 0 AND this3.number IN $param0) - RETURN count(this2) = 1 AS var5 - } - WITH * - WHERE (this1.estateType IN $param1 AND this1.area >= $param2 AND this1.floor >= $param3 AND var5 = true) - RETURN count(this1) = 1 AS var6 + WHERE EXISTS { + MATCH (this2)-[:HAS_POSTAL_CODE]->(this3:PostalCode) + WHERE this3.number IN $param4 + } + }) } - WITH * - WHERE var6 = true - RETURN count(this0) = 1 AS var7 - } - WITH * - WHERE ((this.price >= $param4 AND var7 = true) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) + }) AND ($isAuthenticated = true AND this.archivedAt IS NULL)) WITH * SKIP $param6 LIMIT $param7 CALL { WITH this - MATCH (this)-[this8:HAS_VALUATION]->(this9:Valuation) + MATCH (this)-[this4:HAS_VALUATION]->(this5:Valuation) + WITH DISTINCT this5 WITH * - WHERE ($isAuthenticated = true AND this9.archivedAt IS NULL) + WHERE ($isAuthenticated = true AND this5.archivedAt IS NULL) CALL { - WITH this9 - MATCH (this9)-[this10:VALUATION_FOR]->(this11:Estate) + WITH this5 + MATCH (this5)-[this6:VALUATION_FOR]->(this7:Estate) + WITH DISTINCT this7 WITH * - WHERE ($isAuthenticated = true AND this11.archivedAt IS NULL) - WITH this11 { .uuid } AS this11 - RETURN head(collect(this11)) AS var12 + WHERE ($isAuthenticated = true AND this7.archivedAt IS NULL) + WITH this7 { .uuid } AS this7 + RETURN collect(this7) AS var8 } - WITH this9 { estate: var12 } AS this9 - RETURN head(collect(this9)) AS var13 + WITH this5 { estate: var8 } AS this5 + RETURN collect(this5) AS var9 } - RETURN this { valuation: var13 } AS this" + RETURN this { valuation: var9 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": [ - \\"13001\\" - ], + \\"param0\\": 0, \\"param1\\": [ \\"APARTMENT\\" ], @@ -576,7 +596,9 @@ describe("https://github.com/neo4j/graphql/issues/2396", () => { \\"low\\": 0, \\"high\\": 0 }, - \\"param4\\": 0, + \\"param4\\": [ + \\"13001\\" + ], \\"isAuthenticated\\": true, \\"param6\\": { \\"low\\": 20, diff --git a/packages/graphql/tests/tck/issues/2437.test.ts b/packages/graphql/tests/tck/issues/2437.test.ts index e06a472ed7..ed9dafe4d6 100644 --- a/packages/graphql/tests/tck/issues/2437.test.ts +++ b/packages/graphql/tests/tck/issues/2437.test.ts @@ -32,24 +32,24 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { } type Agent @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime valuations: [Valuation!]! @relationship(type: "IS_VALUATION_AGENT", direction: OUT) } extend type Agent @authorization( - validate: [{ operations: [CREATE], where: { jwt: { roles_INCLUDES: "Admin" } } }] - filter: [{ where: { node: { archivedAt_EQ: null } } }] + validate: [{ operations: [CREATE], where: { jwt: { roles: { includes: "Admin" } } } }] + filter: [{ where: { node: { archivedAt: { eq: null } } } }] ) type Valuation @mutation(operations: [CREATE, UPDATE]) @node { - uuid: ID! @id @unique + uuid: ID! @id archivedAt: DateTime - agent: Agent! @relationship(type: "IS_VALUATION_AGENT", direction: IN) + agent: [Agent!]! @relationship(type: "IS_VALUATION_AGENT", direction: IN) } - extend type Valuation @authorization(filter: [{ where: { node: { archivedAt_EQ: null } } }]) + extend type Valuation @authorization(filter: [{ where: { node: { archivedAt: { eq: null } } } }]) `; neoSchema = new Neo4jGraphQL({ @@ -61,7 +61,7 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { test("query and limits nested connections", async () => { const query = /* GraphQL */ ` query Agents { - agents(where: { uuid_EQ: "a1" }) { + agents(where: { uuid: { eq: "a1" } }) { uuid valuationsConnection(first: 10) { edges { @@ -81,7 +81,8 @@ describe("https://github.com/neo4j/graphql/issues/2437", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Agent) + "CYPHER 5 + MATCH (this:Agent) WITH * WHERE (this.uuid = $param0 AND ($isAuthenticated = true AND this.archivedAt IS NULL)) CALL { diff --git a/packages/graphql/tests/tck/issues/2581.test.ts b/packages/graphql/tests/tck/issues/2581.test.ts index 105dabc857..0ca9150a9b 100644 --- a/packages/graphql/tests/tck/issues/2581.test.ts +++ b/packages/graphql/tests/tck/issues/2581.test.ts @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { type Book @node { name: String! year: Int - refID: ID @id @unique + refID: ID @id soldCopies: Int @cypher( statement: "OPTIONAL MATCH(sales:Sales) WHERE this.refID = sales.refID WITH count(sales) as result RETURN result as result" @@ -90,7 +90,8 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { @@ -134,7 +135,8 @@ describe("https://github.com/neo4j/graphql/issues/2581", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Author) + "CYPHER 5 + MATCH (this:Author) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/2614.test.ts b/packages/graphql/tests/tck/issues/2614.test.ts index 8e07f1da92..b1a4d36aa4 100644 --- a/packages/graphql/tests/tck/issues/2614.test.ts +++ b/packages/graphql/tests/tck/issues/2614.test.ts @@ -62,7 +62,7 @@ describe("https://github.com/neo4j/graphql/issues/2614", () => { const query = /* GraphQL */ ` query GetProductionsMovie { actors { - actedIn(where: { title_EQ: "Test Movie" }) { + actedIn(where: { title: { eq: "Test Movie" } }) { title } } @@ -71,7 +71,8 @@ describe("https://github.com/neo4j/graphql/issues/2614", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/2670.test.ts b/packages/graphql/tests/tck/issues/2670.test.ts index 633557cd95..ae11391787 100644 --- a/packages/graphql/tests/tck/issues/2670.test.ts +++ b/packages/graphql/tests/tck/issues/2670.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where moviesAggregate count equal", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SOME: { node: { moviesAggregate: { count_EQ: 2 } } } }) { + movies(where: { genresConnection: { some: { node: { moviesAggregate: { count: { eq: 2 } } } } } }) { title } } @@ -63,7 +63,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -94,7 +95,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where moviesAggregate count_LT", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SOME: { node: { moviesAggregate: { count_LT: 3 } } } }) { + movies(where: { genresConnection: { some: { node: { moviesAggregate: { count: { lt: 3 } } } } } }) { title } } @@ -103,7 +104,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -134,7 +136,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where moviesAggregate count_GT", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SOME: { node: { moviesAggregate: { count_GT: 2 } } } }) { + movies(where: { genresConnection: { some: { node: { moviesAggregate: { count: { gt: 2 } } } } } }) { title } } @@ -143,7 +145,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -176,8 +179,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { - node: { moviesAggregate: { node: { title_SHORTEST_LENGTH_EQUAL: 5 } } } + genresConnection: { + some: { node: { moviesAggregate: { node: { title: { shortestLength: { eq: 5 } } } } } } } } ) { @@ -189,7 +192,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -222,8 +226,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { - node: { moviesAggregate: { node: { title_AVERAGE_LENGTH_EQUAL: 1 } } } + genresConnection: { + some: { node: { moviesAggregate: { node: { title: { averageLength: { eq: 1 } } } } } } } } ) { @@ -235,7 +239,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -264,7 +269,11 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const query = /* GraphQL */ ` { movies( - where: { genresConnection_SOME: { node: { moviesAggregate: { edge: { intValue_MAX_LT: 983 } } } } } + where: { + genresConnection: { + some: { node: { moviesAggregate: { edge: { intValue: { max: { lt: 983 } } } } } } + } + } ) { title } @@ -274,7 +283,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -306,7 +316,11 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const query = /* GraphQL */ ` { movies( - where: { genresConnection_SOME: { node: { moviesAggregate: { edge: { intValue_MIN_EQUAL: 1 } } } } } + where: { + genresConnection: { + some: { node: { moviesAggregate: { edge: { intValue: { min: { eq: 1 } } } } } } + } + } ) { title } @@ -316,7 +330,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -347,7 +362,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where genresConnection_SOME", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SOME: { node: { moviesAggregate: { count_EQ: 2 } } } }) { + movies(where: { genresConnection: { some: { node: { moviesAggregate: { count: { eq: 2 } } } } } }) { title } } @@ -356,7 +371,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -387,7 +403,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where genresConnection_NONE", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_NONE: { node: { moviesAggregate: { count_EQ: 2 } } } }) { + movies(where: { genresConnection: { none: { node: { moviesAggregate: { count: { eq: 2 } } } } } }) { title } } @@ -396,7 +412,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -427,7 +444,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where genresConnection_ALL", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_ALL: { node: { moviesAggregate: { count_EQ: 2 } } } }) { + movies(where: { genresConnection: { all: { node: { moviesAggregate: { count: { eq: 2 } } } } } }) { title } } @@ -436,7 +453,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -483,7 +501,7 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { test("should find where genresConnection_SINGLE", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_SINGLE: { node: { moviesAggregate: { count_EQ: 2 } } } }) { + movies(where: { genresConnection: { single: { node: { moviesAggregate: { count: { eq: 2 } } } } } }) { title } } @@ -492,7 +510,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -525,11 +544,13 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { - AND: [ - { node: { moviesAggregate: { count_EQ: 2 } } } - { node: { seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 1 } } } } - ] + genresConnection: { + some: { + AND: [ + { node: { moviesAggregate: { count: { eq: 2 } } } } + { node: { seriesAggregate: { node: { name: { shortestLength: { eq: 1 } } } } } } + ] + } } } ) { @@ -541,7 +562,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -583,11 +605,13 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { - OR: [ - { node: { moviesAggregate: { count_EQ: 3 } } } - { node: { seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 983 } } } } - ] + genresConnection: { + some: { + OR: [ + { node: { moviesAggregate: { count: { eq: 3 } } } } + { node: { seriesAggregate: { node: { name: { shortestLength: { eq: 983 } } } } } } + ] + } } } ) { @@ -599,7 +623,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -641,10 +666,12 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { - node: { - moviesAggregate: { count_EQ: 2 } - seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 983 } } + genresConnection: { + some: { + node: { + moviesAggregate: { count: { eq: 2 } } + seriesAggregate: { node: { name: { shortestLength: { eq: 983 } } } } + } } } } @@ -657,7 +684,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -699,8 +727,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { { movies( where: { - genresConnection_SOME: { node: { moviesAggregate: { count_EQ: 3 } } } - genresAggregate: { count_EQ: 1 } + genresConnection: { some: { node: { moviesAggregate: { count: { eq: 3 } } } } } + genresAggregate: { count: { eq: 1 } } } ) { title @@ -711,7 +739,8 @@ describe("https://github.com/neo4j/graphql/issues/2670", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) diff --git a/packages/graphql/tests/tck/issues/2708.test.ts b/packages/graphql/tests/tck/issues/2708.test.ts index 48cc22f3d0..ed774133b8 100644 --- a/packages/graphql/tests/tck/issues/2708.test.ts +++ b/packages/graphql/tests/tck/issues/2708.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate count equal", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { count_EQ: 2 } } }) { + movies(where: { genres: { some: { moviesAggregate: { count: { eq: 2 } } } } }) { title } } @@ -63,7 +63,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -94,7 +95,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate count_LT", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { count_LT: 3 } } }) { + movies(where: { genres: { some: { moviesAggregate: { count: { lt: 3 } } } } }) { title } } @@ -103,7 +104,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -134,7 +136,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate count_GT", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { count_GT: 2 } } }) { + movies(where: { genres: { some: { moviesAggregate: { count: { gt: 2 } } } } }) { title } } @@ -143,7 +145,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -174,7 +177,9 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate node property SHORTEST", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { node: { title_SHORTEST_LENGTH_EQUAL: 1 } } } }) { + movies( + where: { genres: { some: { moviesAggregate: { node: { title: { shortestLength: { eq: 1 } } } } } } } + ) { title } } @@ -183,7 +188,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -214,7 +220,9 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate node property AVERAGE", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { node: { title_AVERAGE_LENGTH_EQUAL: 1 } } } }) { + movies( + where: { genres: { some: { moviesAggregate: { node: { title: { averageLength: { eq: 1 } } } } } } } + ) { title } } @@ -223,7 +231,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -251,7 +260,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate edge property MAX_LT", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { edge: { intValue_MAX_LT: 1 } } } }) { + movies(where: { genres: { some: { moviesAggregate: { edge: { intValue: { max: { lt: 1 } } } } } } }) { title } } @@ -260,7 +269,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -291,7 +301,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where moviesAggregate edge property MIN_EQUAL", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { edge: { intValue_MIN_EQUAL: 1 } } } }) { + movies(where: { genres: { some: { moviesAggregate: { edge: { intValue: { min: { eq: 1 } } } } } } }) { title } } @@ -300,7 +310,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -331,7 +342,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where genres_SOME", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { count_EQ: 2 } } }) { + movies(where: { genres: { some: { moviesAggregate: { count: { eq: 2 } } } } }) { title } } @@ -340,7 +351,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -371,7 +383,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where genres_NONE", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_NONE: { moviesAggregate: { count_EQ: 2 } } }) { + movies(where: { genres: { none: { moviesAggregate: { count: { eq: 2 } } } } }) { title } } @@ -380,7 +392,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -411,7 +424,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where genres_ALL", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_ALL: { moviesAggregate: { count_EQ: 2 } } }) { + movies(where: { genres: { all: { moviesAggregate: { count: { eq: 2 } } } } }) { title } } @@ -420,7 +433,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -467,7 +481,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find where genres_SINGLE", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SINGLE: { moviesAggregate: { count_EQ: 2 } } }) { + movies(where: { genres: { single: { moviesAggregate: { count: { eq: 2 } } } } }) { title } } @@ -476,7 +490,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -507,7 +522,7 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should not find genres_ALL where NONE true", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_ALL: { moviesAggregate: { count_EQ: 0 } } }) { + movies(where: { genres: { all: { moviesAggregate: { count: { eq: 0 } } } } }) { title } } @@ -516,7 +531,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -565,11 +581,13 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { { movies( where: { - genres_SOME: { - AND: [ - { moviesAggregate: { count_EQ: 2 } } - { seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 1 } } } - ] + genres: { + some: { + AND: [ + { moviesAggregate: { count: { eq: 2 } } } + { seriesAggregate: { node: { name: { shortestLength: { eq: 1 } } } } } + ] + } } } ) { @@ -581,7 +599,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -623,11 +642,13 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { { movies( where: { - genres_SOME: { - OR: [ - { moviesAggregate: { count_EQ: 3 } } - { seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 1 } } } - ] + genres: { + some: { + OR: [ + { moviesAggregate: { count: { eq: 3 } } } + { seriesAggregate: { node: { name: { shortestLength: { eq: 1 } } } } } + ] + } } } ) { @@ -639,7 +660,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -681,9 +703,11 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { { movies( where: { - genres_SOME: { - moviesAggregate: { count_EQ: 2 } - seriesAggregate: { node: { name_SHORTEST_LENGTH_EQUAL: 1 } } + genres: { + some: { + moviesAggregate: { count: { eq: 2 } } + seriesAggregate: { node: { name: { shortestLength: { eq: 1 } } } } + } } } ) { @@ -695,7 +719,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -735,7 +760,12 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { test("should find genres with aggregation at the same level", async () => { const query = /* GraphQL */ ` { - movies(where: { genres_SOME: { moviesAggregate: { count_EQ: 3 } }, genresAggregate: { count_EQ: 1 } }) { + movies( + where: { + genres: { some: { moviesAggregate: { count: { eq: 3 } } } } + genresAggregate: { count: { eq: 1 } } + } + ) { title } } @@ -744,7 +774,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -786,7 +817,14 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { { movies( where: { - genres_ALL: { OR: [{ moviesAggregate: { count_EQ: 0 } }, { seriesAggregate: { count_EQ: 1 } }] } + genres: { + all: { + OR: [ + { moviesAggregate: { count: { eq: 0 } } } + { seriesAggregate: { count: { eq: 1 } } } + ] + } + } } ) { title @@ -797,7 +835,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) @@ -864,9 +903,11 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { { movies( where: { - genres_ALL: { - OR: [{ moviesAggregate: { count_EQ: 0 } }, { name_EQ: "Thriller" }] - seriesAggregate: { count_EQ: 1 } + genres: { + all: { + OR: [{ moviesAggregate: { count: { eq: 0 } } }, { name: { eq: "Thriller" } }] + seriesAggregate: { count: { eq: 1 } } + } } } ) { @@ -878,7 +919,8 @@ describe("https://github.com/neo4j/graphql/issues/2708", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[:IN_GENRE]->(this0:Genre) diff --git a/packages/graphql/tests/tck/issues/2709.test.ts b/packages/graphql/tests/tck/issues/2709.test.ts index 3d74d69476..ac4dd5d8f0 100644 --- a/packages/graphql/tests/tck/issues/2709.test.ts +++ b/packages/graphql/tests/tck/issues/2709.test.ts @@ -94,7 +94,7 @@ describe("https://github.com/neo4j/graphql/issues/2709", () => { test("should not use a node label so it covers all nodes implementing the interface for connection rel", async () => { const query = /* GraphQL */ ` query { - movies(where: { distributionConnection_SOME: { node: { name_EQ: "test4" } } }) { + movies(where: { distributionConnection: { some: { node: { name: { eq: "test4" } } } } }) { title } } @@ -102,7 +102,8 @@ describe("https://github.com/neo4j/graphql/issues/2709", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[this0:DISTRIBUTED_BY]-(this1) WHERE (this1.name = $param0 AND (this1:Dishney OR this1:Prime OR this1:Netflix)) @@ -121,7 +122,11 @@ describe("https://github.com/neo4j/graphql/issues/2709", () => { const query = /* GraphQL */ ` query { movies( - where: { distributionConnection_SOME: { node: { OR: [{ name_EQ: "test4" }, { name_EQ: "test1" }] } } } + where: { + distributionConnection: { + some: { node: { OR: [{ name: { eq: "test4" } }, { name: { eq: "test1" } }] } } + } + } ) { title } @@ -130,7 +135,8 @@ describe("https://github.com/neo4j/graphql/issues/2709", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[this0:DISTRIBUTED_BY]-(this1) WHERE ((this1.name = $param0 OR this1.name = $param1) AND (this1:Dishney OR this1:Prime OR this1:Netflix)) @@ -216,7 +222,11 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { test("should use the correct node label for connection rel when defined in node _on - Netflix label", async () => { const query = /* GraphQL */ ` query { - movies(where: { OR: [{ distributionConnection_SOME: { Netflix: { node: { name_EQ: "test" } } } }] }) { + movies( + where: { + OR: [{ distributionConnection: { some: { Netflix: { node: { name: { eq: "test" } } } } } }] + } + ) { title } } @@ -224,7 +234,8 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[this0:DISTRIBUTED_BY]-(this1:Netflix) WHERE this1.name = $param0 @@ -242,7 +253,11 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { test("should use the correct node label for connection rel when defined in node _on - Dishney label", async () => { const query = /* GraphQL */ ` query { - movies(where: { OR: [{ distributionConnection_SOME: { Dishney: { node: { name_EQ: "test2" } } } }] }) { + movies( + where: { + OR: [{ distributionConnection: { some: { Dishney: { node: { name: { eq: "test2" } } } } } }] + } + ) { title } } @@ -250,7 +265,8 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[this0:DISTRIBUTED_BY]-(this1:Dishney) WHERE this1.name = $param0 @@ -268,7 +284,7 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { test("should use the correct node label for connection rel when defined in node _on - without OR operator", async () => { const query = /* GraphQL */ ` query { - movies(where: { distributionConnection_SOME: { Dishney: { node: { name_EQ: "test3" } } } }) { + movies(where: { distributionConnection: { some: { Dishney: { node: { name: { eq: "test3" } } } } } }) { title } } @@ -276,7 +292,8 @@ describe("https://github.com/neo4j/graphql/issues/2709 union parity", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Film) + "CYPHER 5 + MATCH (this:Film) WHERE EXISTS { MATCH (this)<-[this0:DISTRIBUTED_BY]-(this1:Dishney) WHERE this1.name = $param0 diff --git a/packages/graphql/tests/tck/issues/2713.test.ts b/packages/graphql/tests/tck/issues/2713.test.ts index 2e82b8cbd0..99ceaf128f 100644 --- a/packages/graphql/tests/tck/issues/2713.test.ts +++ b/packages/graphql/tests/tck/issues/2713.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { test("should not find genresConnection_ALL where NONE true", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_ALL: { node: { moviesAggregate: { count_EQ: 0 } } } }) { + movies(where: { genresConnection: { all: { node: { moviesAggregate: { count: { eq: 0 } } } } } }) { title } } @@ -63,7 +63,8 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -111,7 +112,11 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { const query = /* GraphQL */ ` { movies( - where: { genresConnection_ALL: { node: { moviesAggregate: { count_EQ: 0 }, name_EQ: "Thriller" } } } + where: { + genresConnection: { + all: { node: { moviesAggregate: { count: { eq: 0 } }, name: { eq: "Thriller" } } } + } + } ) { title } @@ -121,7 +126,8 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:IN_GENRE]->(this1:Genre) @@ -170,7 +176,7 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { test("should not find genresConnection_ALL by genre title", async () => { const query = /* GraphQL */ ` { - movies(where: { genresConnection_ALL: { node: { name_EQ: "Thriller" } } }) { + movies(where: { genresConnection: { all: { node: { name: { eq: "Thriller" } } } } }) { title } } @@ -179,7 +185,8 @@ describe("https://github.com/neo4j/graphql/issues/2713", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (EXISTS { MATCH (this)-[this0:IN_GENRE]->(this1:Genre) WHERE this1.name = $param0 diff --git a/packages/graphql/tests/tck/issues/2766.test.ts b/packages/graphql/tests/tck/issues/2766.test.ts index 19cbbec9dc..26331bdaa0 100644 --- a/packages/graphql/tests/tck/issues/2766.test.ts +++ b/packages/graphql/tests/tck/issues/2766.test.ts @@ -69,7 +69,8 @@ describe("https://github.com/neo4j/graphql/issues/2766", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { @@ -82,6 +83,7 @@ describe("https://github.com/neo4j/graphql/issues/2766", () => { CALL { WITH this0 MATCH (this0)<-[this1:ACTED_IN]-(this2:Actor) + WITH DISTINCT this2 CALL { WITH this2 CALL { diff --git a/packages/graphql/tests/tck/issues/2782.test.ts b/packages/graphql/tests/tck/issues/2782.test.ts index 7ee3095582..e66165aa26 100644 --- a/packages/graphql/tests/tck/issues/2782.test.ts +++ b/packages/graphql/tests/tck/issues/2782.test.ts @@ -39,7 +39,7 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { type Photo @node { id: ID! - color: Color @relationship(type: "OF_COLOR", direction: OUT) + color: [Color!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -59,12 +59,12 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { colors: { disconnect: [ { - where: { node: { name_EQ: "Red" } } + where: { node: { name: { eq: "Red" } } } disconnect: { photos: [ { - where: { node: { id_EQ: "123" } } - disconnect: { color: { where: { node: { id_EQ: "134" } } } } + where: { node: { id: { eq: "123" } } } + disconnect: { color: { where: { node: { id: { eq: "134" } } } } } } ] } @@ -74,12 +74,12 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { photos: { disconnect: [ { - where: { node: { id_EQ: "321" } } - disconnect: { color: { where: { node: { name_EQ: "Green" } } } } + where: { node: { id: { eq: "321" } } } + disconnect: { color: { where: { node: { name: { eq: "Green" } } } } } } { - where: { node: { id_EQ: "33211" } } - disconnect: { color: { where: { node: { name_EQ: "Red" } } } } + where: { node: { id: { eq: "33211" } } } + disconnect: { color: { where: { node: { name: { eq: "Red" } } } } } } ] } @@ -95,7 +95,8 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Product) + "CYPHER 5 + MATCH (this:Product) SET this.id = $this_update_id_SET SET this.name = $this_update_name_SET WITH this @@ -122,7 +123,7 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { CALL { WITH this, this_colors0_disconnect0, this_colors0_disconnect0_photos0 OPTIONAL MATCH (this_colors0_disconnect0_photos0)-[this_colors0_disconnect0_photos0_color0_rel:OF_COLOR]->(this_colors0_disconnect0_photos0_color0:Color) - WHERE this_colors0_disconnect0_photos0_color0.id = $updateProducts_args_update_colors0_disconnect0_disconnect_photos_disconnect_color_where_Color_this_colors0_disconnect0_photos0_color0param0 + WHERE this_colors0_disconnect0_photos0_color0.id = $updateProducts_args_update_colors0_disconnect0_disconnect_photos0_disconnect_color0_where_Color_this_colors0_disconnect0_photos0_color0param0 CALL { WITH this_colors0_disconnect0_photos0_color0, this_colors0_disconnect0_photos0_color0_rel, this_colors0_disconnect0_photos0 WITH collect(this_colors0_disconnect0_photos0_color0) as this_colors0_disconnect0_photos0_color0, this_colors0_disconnect0_photos0_color0_rel, this_colors0_disconnect0_photos0 @@ -149,7 +150,7 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { CALL { WITH this, this_photos0_disconnect0 OPTIONAL MATCH (this_photos0_disconnect0)-[this_photos0_disconnect0_color0_rel:OF_COLOR]->(this_photos0_disconnect0_color0:Color) - WHERE this_photos0_disconnect0_color0.name = $updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect0_color0param0 + WHERE this_photos0_disconnect0_color0.name = $updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect0_color0param0 CALL { WITH this_photos0_disconnect0_color0, this_photos0_disconnect0_color0_rel, this_photos0_disconnect0 WITH collect(this_photos0_disconnect0_color0) as this_photos0_disconnect0_color0, this_photos0_disconnect0_color0_rel, this_photos0_disconnect0 @@ -174,7 +175,7 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { CALL { WITH this, this_photos0_disconnect1 OPTIONAL MATCH (this_photos0_disconnect1)-[this_photos0_disconnect1_color0_rel:OF_COLOR]->(this_photos0_disconnect1_color0:Color) - WHERE this_photos0_disconnect1_color0.name = $updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect1_color0param0 + WHERE this_photos0_disconnect1_color0.name = $updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect1_color0param0 CALL { WITH this_photos0_disconnect1_color0, this_photos0_disconnect1_color0_rel, this_photos0_disconnect1 WITH collect(this_photos0_disconnect1_color0) as this_photos0_disconnect1_color0, this_photos0_disconnect1_color0_rel, this_photos0_disconnect1 @@ -194,11 +195,11 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { \\"this_update_name_SET\\": \\"Nested Connect\\", \\"updateProducts_args_update_colors0_disconnect0_where_Color_this_colors0_disconnect0param0\\": \\"Red\\", \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos0_where_Photo_this_colors0_disconnect0_photos0param0\\": \\"123\\", - \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos_disconnect_color_where_Color_this_colors0_disconnect0_photos0_color0param0\\": \\"134\\", + \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos0_disconnect_color0_where_Color_this_colors0_disconnect0_photos0_color0param0\\": \\"134\\", \\"updateProducts_args_update_photos0_disconnect0_where_Photo_this_photos0_disconnect0param0\\": \\"321\\", - \\"updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect0_color0param0\\": \\"Green\\", + \\"updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect0_color0param0\\": \\"Green\\", \\"updateProducts_args_update_photos0_disconnect1_where_Photo_this_photos0_disconnect1param0\\": \\"33211\\", - \\"updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect1_color0param0\\": \\"Red\\", + \\"updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect1_color0param0\\": \\"Red\\", \\"updateProducts\\": { \\"args\\": { \\"update\\": { @@ -210,7 +211,9 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Red\\" + \\"name\\": { + \\"eq\\": \\"Red\\" + } } }, \\"disconnect\\": { @@ -218,17 +221,23 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"123\\" + \\"id\\": { + \\"eq\\": \\"123\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"134\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"134\\" + } + } } } - } + ] } } ] @@ -243,33 +252,45 @@ describe("https://github.com/neo4j/graphql/issues/2782", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"321\\" + \\"id\\": { + \\"eq\\": \\"321\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Green\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Green\\" + } + } } } - } + ] } }, { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"33211\\" + \\"id\\": { + \\"eq\\": \\"33211\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Red\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Red\\" + } + } } } - } + ] } } ] diff --git a/packages/graphql/tests/tck/issues/2789.test.ts b/packages/graphql/tests/tck/issues/2789.test.ts index cb11e62a37..0efe581218 100644 --- a/packages/graphql/tests/tck/issues/2789.test.ts +++ b/packages/graphql/tests/tck/issues/2789.test.ts @@ -23,9 +23,9 @@ import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-ut describe("https://github.com/neo4j/graphql/issues/2789", () => { let neoSchema: Neo4jGraphQL; const typeDefs = /* GraphQL */ ` - type User @authorization(validate: [{ where: { node: { id_EQ: "Foo" } } }]) @node { + type User @authorization(validate: [{ where: { node: { id: { eq: "Foo" } } } }]) @node { id: ID - password: String! @authorization(validate: [{ where: { node: { id_EQ: "Bar" } } }]) + password: String! @authorization(validate: [{ where: { node: { id: { eq: "Bar" } } } }]) } `; @@ -50,7 +50,8 @@ describe("https://github.com/neo4j/graphql/issues/2789", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($param1 IS NOT NULL AND this.id = $param1)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this diff --git a/packages/graphql/tests/tck/issues/2803.test.ts b/packages/graphql/tests/tck/issues/2803.test.ts index 404443fb47..ef0cd61e17 100644 --- a/packages/graphql/tests/tck/issues/2803.test.ts +++ b/packages/graphql/tests/tck/issues/2803.test.ts @@ -49,7 +49,7 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { test("should find movies aggregate within double nested relationships", async () => { const query = /* GraphQL */ ` { - actors(where: { movies_SOME: { actors_ALL: { moviesAggregate: { count_GT: 1 } } } }) { + actors(where: { movies: { some: { actors: { all: { moviesAggregate: { count: { gt: 1 } } } } } } }) { name } } @@ -58,7 +58,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -114,9 +115,11 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - movies_SOME: { - actors_ALL: { moviesAggregate: { count_GT: 1 } } - actorsAggregate: { count_EQ: 1 } + movies: { + some: { + actors: { all: { moviesAggregate: { count: { gt: 1 } } } } + actorsAggregate: { count: { eq: 1 } } + } } } ) { @@ -128,7 +131,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -191,7 +195,13 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { test("should find movies aggregate within triple nested relationships", async () => { const query = /* GraphQL */ ` { - movies(where: { actors_SOME: { movies_SOME: { actors_ALL: { moviesAggregate: { count_GT: 2 } } } } }) { + movies( + where: { + actors: { + some: { movies: { some: { actors: { all: { moviesAggregate: { count: { gt: 2 } } } } } } } + } + } + ) { released } } @@ -200,7 +210,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[:ACTED_IN]-(this0:Actor) @@ -263,14 +274,18 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { movies( where: { - actors_SINGLE: { - movies_SOME: { - actors_ALL: { moviesAggregate: { count_GT: 1 } } - actorsAggregate: { node: { name_AVERAGE_LENGTH_LT: 10 } } + actors: { + single: { + movies: { + some: { + actors: { all: { moviesAggregate: { count: { gt: 1 } } } } + actorsAggregate: { node: { name: { averageLength: { lt: 10 } } } } + } + } + moviesAggregate: { node: { released: { average: { eq: 25 } } } } } - moviesAggregate: { node: { released_AVERAGE_EQUAL: 25 } } } - actorsAggregate: { node: { name_AVERAGE_LENGTH_GTE: 3 } } + actorsAggregate: { node: { name: { averageLength: { gte: 3 } } } } } ) { released @@ -281,7 +296,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[:ACTED_IN]-(this0:Actor) @@ -362,8 +378,10 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - moviesConnection_SOME: { - node: { actorsConnection_ALL: { node: { moviesAggregate: { count_GT: 1 } } } } + moviesConnection: { + some: { + node: { actorsConnection: { all: { node: { moviesAggregate: { count: { gt: 1 } } } } } } + } } } ) { @@ -375,7 +393,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -431,9 +450,11 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - movies_SOME: { - actorsConnection_ALL: { node: { moviesAggregate: { count_GT: 1 } } } - actorsAggregate: { count_EQ: 1 } + movies: { + some: { + actorsConnection: { all: { node: { moviesAggregate: { count: { gt: 1 } } } } } + actorsAggregate: { count: { eq: 1 } } + } } } ) { @@ -445,7 +466,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -510,10 +532,18 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { movies( where: { - actorsConnection_SOME: { - node: { - moviesConnection_SOME: { - node: { actorsConnection_ALL: { node: { moviesAggregate: { count_GT: 2 } } } } + actorsConnection: { + some: { + node: { + moviesConnection: { + some: { + node: { + actorsConnection: { + all: { node: { moviesAggregate: { count: { gt: 2 } } } } + } + } + } + } } } } @@ -527,7 +557,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -590,18 +621,24 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { movies( where: { - actorsConnection_SOME: { - node: { - moviesConnection_SOME: { - node: { - actorsConnection_ALL: { node: { moviesAggregate: { count_GT: 1 } } } - actorsAggregate: { node: { name_AVERAGE_LENGTH_LT: 10 } } + actorsConnection: { + some: { + node: { + moviesConnection: { + some: { + node: { + actorsConnection: { + all: { node: { moviesAggregate: { count: { gt: 1 } } } } + } + actorsAggregate: { node: { name: { averageLength: { lt: 10 } } } } + } + } } + moviesAggregate: { node: { released: { average: { eq: 25 } } } } } - moviesAggregate: { node: { released_AVERAGE_EQUAL: 25 } } } } - actorsAggregate: { node: { name_AVERAGE_LENGTH_GTE: 3 } } + actorsAggregate: { node: { name: { averageLength: { gte: 3 } } } } } ) { released @@ -612,7 +649,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -692,7 +730,11 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const query = /* GraphQL */ ` { actors( - where: { movies_SOME: { actorsConnection_ALL: { node: { moviesAggregate: { count_GT: 1 } } } } } + where: { + movies: { + some: { actorsConnection: { all: { node: { moviesAggregate: { count: { gt: 1 } } } } } } + } + } ) { name } @@ -702,7 +744,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -757,7 +800,11 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const query = /* GraphQL */ ` { actors( - where: { moviesConnection_SOME: { node: { actors_ALL: { moviesAggregate: { count_GT: 1 } } } } } + where: { + moviesConnection: { + some: { node: { actors: { all: { moviesAggregate: { count: { gt: 1 } } } } } } + } + } ) { name } @@ -767,7 +814,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -823,9 +871,17 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { movies( where: { - actorsConnection_SOME: { - node: { - movies_SINGLE: { actorsConnection_NONE: { node: { moviesAggregate: { count_GT: 2 } } } } + actorsConnection: { + some: { + node: { + movies: { + single: { + actorsConnection: { + none: { node: { moviesAggregate: { count: { gt: 2 } } } } + } + } + } + } } } } @@ -838,7 +894,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) @@ -885,9 +942,13 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - movies_SINGLE: { - actors_NONE: { moviesAggregate: { edge: { screenTime_AVERAGE_LTE: 1000 } } } - actorsAggregate: { edge: { screenTime_AVERAGE_LTE: 1000 } } + movies: { + single: { + actors: { + none: { moviesAggregate: { edge: { screenTime: { average: { lte: 1000 } } } } } + } + actorsAggregate: { edge: { screenTime: { average: { lte: 1000 } } } } + } } } ) { @@ -899,7 +960,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -942,14 +1004,18 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - moviesConnection_SINGLE: { - node: { - actorsConnection_NONE: { - node: { moviesAggregate: { count_GT: 1 } } - edge: { roles_INCLUDES: "a role" } + moviesConnection: { + single: { + node: { + actorsConnection: { + none: { + node: { moviesAggregate: { count: { gt: 1 } } } + edge: { roles: { includes: "a role" } } + } + } } + edge: { roles: { includes: "another role" } } } - edge: { roles_INCLUDES: "another role" } } } ) { @@ -961,7 +1027,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -1003,11 +1070,15 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - moviesConnection_SINGLE: { - node: { - actorsConnection_SOME: { - node: { name_EQ: "actor name", moviesAggregate: { count_GT: 1 } } - edge: { roles_INCLUDES: "actor role" } + moviesConnection: { + single: { + node: { + actorsConnection: { + some: { + node: { name: { eq: "actor name" }, moviesAggregate: { count: { gt: 1 } } } + edge: { roles: { includes: "actor role" } } + } + } } } } @@ -1021,7 +1092,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -1062,7 +1134,11 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const query = /* GraphQL */ ` { actors( - where: { movies_ALL: { actors_SOME: { name_EQ: "a name", moviesAggregate: { count_GT: 1 } } } } + where: { + movies: { + all: { actors: { some: { name: { eq: "a name" }, moviesAggregate: { count: { gt: 1 } } } } } + } + } ) { name } @@ -1072,7 +1148,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -1137,8 +1214,14 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - movies_ALL: { - actors_SOME: { OR: [{ name_EQ: "some name" }, { moviesAggregate: { count_GT: 1 } }] } + movies: { + all: { + actors: { + some: { + OR: [{ name: { eq: "some name" } }, { moviesAggregate: { count: { gt: 1 } } }] + } + } + } } } ) { @@ -1150,7 +1233,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -1215,8 +1299,14 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { { actors( where: { - movies_ALL: { - actors_SOME: { AND: [{ name_EQ: "some name" }, { moviesAggregate: { count_GT: 1 } }] } + movies: { + all: { + actors: { + some: { + AND: [{ name: { eq: "some name" } }, { moviesAggregate: { count: { gt: 1 } } }] + } + } + } } } ) { @@ -1228,7 +1318,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[:ACTED_IN]->(this0:Movie) @@ -1293,14 +1384,18 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { mutation { updateActors( where: { - moviesConnection_SINGLE: { - node: { - actorsConnection_NONE: { - node: { moviesAggregate: { count_GT: 1 } } - edge: { roles_INCLUDES: "some role" } + moviesConnection: { + single: { + node: { + actorsConnection: { + none: { + node: { moviesAggregate: { count: { gt: 1 } } } + edge: { roles: { includes: "some role" } } + } + } } + edge: { roles: { includes: "another role" } } } - edge: { roles_INCLUDES: "another role" } } } update: { name_SET: "Exciting new name!" } @@ -1315,7 +1410,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) @@ -1360,11 +1456,15 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { mutation { deleteActors( where: { - moviesConnection_SINGLE: { - node: { - actorsConnection_SOME: { - node: { name_EQ: "a name", moviesAggregate: { count_GT: 1 } } - edge: { roles_INCLUDES: "some-role" } + moviesConnection: { + single: { + node: { + actorsConnection: { + some: { + node: { name: { eq: "a name" }, moviesAggregate: { count: { gt: 1 } } } + edge: { roles: { includes: "some-role" } } + } + } } } } @@ -1378,7 +1478,8 @@ describe("https://github.com/neo4j/graphql/issues/2803", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Movie) diff --git a/packages/graphql/tests/tck/issues/2812.test.ts b/packages/graphql/tests/tck/issues/2812.test.ts index c2d125bc95..d1b59d36f7 100644 --- a/packages/graphql/tests/tck/issues/2812.test.ts +++ b/packages/graphql/tests/tck/issues/2812.test.ts @@ -29,24 +29,26 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { } type Actor - @authorization(validate: [{ when: [BEFORE], where: { node: { nodeCreatedBy_EQ: "$jwt.sub" } } }]) + @authorization(validate: [{ when: [BEFORE], where: { node: { nodeCreatedBy: { eq: "$jwt.sub" } } } }]) @node { - id: ID! @id @unique + id: ID! @id name: String nodeCreatedBy: String fieldA: String @authorization( - validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "role-A" } } }] + validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles: { includes: "role-A" } } } }] ) fieldB: String @authorization( - validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "role-B" } } }] + validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles: { includes: "role-B" } } } }] ) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT) } type Movie @node - @authorization(validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "admin" } } }]) { + @authorization( + validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles: { includes: "admin" } } } }] + ) { id: ID actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } @@ -82,7 +84,8 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -112,6 +115,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { WITH create_this1 MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this7.nodeCreatedBy = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this7 WITH create_this7 { .name } AS create_this7 RETURN collect(create_this7) AS create_var8 } @@ -192,7 +196,8 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -222,6 +227,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { WITH create_this1 MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this7.nodeCreatedBy = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this7 WITH create_this7 { .name } AS create_this7 RETURN collect(create_this7) AS create_var8 } @@ -300,7 +306,8 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -328,6 +335,7 @@ describe("https://github.com/neo4j/graphql/issues/2812", () => { WITH create_this1 MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this7.nodeCreatedBy = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this7 WITH create_this7 { .name } AS create_this7 RETURN collect(create_this7) AS create_var8 } diff --git a/packages/graphql/tests/tck/issues/2871.test.ts b/packages/graphql/tests/tck/issues/2871.test.ts deleted file mode 100644 index 837b2f8e47..0000000000 --- a/packages/graphql/tests/tck/issues/2871.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/2871", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = /* GraphQL */ ` - type FirstLevel @node { - id: ID! @id @unique - secondLevel: SecondLevel! @relationship(type: "HAS_SECOND_LEVEL", direction: OUT) - createdAt: DateTime! @timestamp(operations: [CREATE]) - } - - type SecondLevel @node { - id: ID! @id @unique - thirdLevel: [ThirdLevel!]! @relationship(type: "HAS_THIRD_LEVEL", direction: OUT) - createdAt: DateTime! @timestamp(operations: [CREATE]) - } - - type ThirdLevel @node { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should be able to filter by SOME nested within single relationship", async () => { - const query = /* GraphQL */ ` - query { - firstLevels(where: { secondLevel: { thirdLevel_SOME: { id_EQ: "3" } } }) { - id - createdAt - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:FirstLevel) - OPTIONAL MATCH (this)-[:HAS_SECOND_LEVEL]->(this0:SecondLevel) - WITH *, count(this0) AS var1 - WITH * - WHERE (var1 <> 0 AND EXISTS { - MATCH (this0)-[:HAS_THIRD_LEVEL]->(this2:ThirdLevel) - WHERE this2.id = $param0 - }) - RETURN this { .id, createdAt: apoc.date.convertFormat(toString(this.createdAt), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"3\\" - }" - `); - }); - - test("should be able to filter by ALL nested within single relationship", async () => { - const query = /* GraphQL */ ` - query { - firstLevels(where: { secondLevel: { thirdLevel_ALL: { id_EQ: "5" } } }) { - id - createdAt - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:FirstLevel) - OPTIONAL MATCH (this)-[:HAS_SECOND_LEVEL]->(this0:SecondLevel) - WITH *, count(this0) AS var1 - WITH * - WHERE (var1 <> 0 AND (EXISTS { - MATCH (this0)-[:HAS_THIRD_LEVEL]->(this2:ThirdLevel) - WHERE this2.id = $param0 - } AND NOT (EXISTS { - MATCH (this0)-[:HAS_THIRD_LEVEL]->(this2:ThirdLevel) - WHERE NOT (this2.id = $param0) - }))) - RETURN this { .id, createdAt: apoc.date.convertFormat(toString(this.createdAt), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"5\\" - }" - `); - }); - - test("should not match if SOME second level relationships meet nested predicates", async () => { - const query = /* GraphQL */ ` - query { - firstLevels(where: { secondLevel: { thirdLevel_NONE: { id_EQ: "25" } } }) { - id - createdAt - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:FirstLevel) - OPTIONAL MATCH (this)-[:HAS_SECOND_LEVEL]->(this0:SecondLevel) - WITH *, count(this0) AS var1 - WITH * - WHERE (var1 <> 0 AND NOT (EXISTS { - MATCH (this0)-[:HAS_THIRD_LEVEL]->(this2:ThirdLevel) - WHERE this2.id = $param0 - })) - RETURN this { .id, createdAt: apoc.date.convertFormat(toString(this.createdAt), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"25\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/288.test.ts b/packages/graphql/tests/tck/issues/288.test.ts index 5db4fb3178..ec982d39c5 100644 --- a/packages/graphql/tests/tck/issues/288.test.ts +++ b/packages/graphql/tests/tck/issues/288.test.ts @@ -57,7 +57,8 @@ describe("#288", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:USER) @@ -84,7 +85,7 @@ describe("#288", () => { test("Can update a USER and COMPANYID is populated", async () => { const query = /* GraphQL */ ` mutation { - updateUsers(where: { USERID_EQ: "userid" }, update: { COMPANYID_SET: "companyid2" }) { + updateUsers(where: { USERID: { eq: "userid" } }, update: { COMPANYID_SET: "companyid2" }) { users { USERID COMPANYID @@ -96,7 +97,8 @@ describe("#288", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:USER) + "CYPHER 5 + MATCH (this:USER) WHERE this.USERID = $param0 SET this.COMPANYID = $this_update_COMPANYID_SET RETURN collect(DISTINCT this { .USERID, .COMPANYID }) AS data" diff --git a/packages/graphql/tests/tck/issues/2925.test.ts b/packages/graphql/tests/tck/issues/2925.test.ts index a29c34d366..9917d51c07 100644 --- a/packages/graphql/tests/tck/issues/2925.test.ts +++ b/packages/graphql/tests/tck/issues/2925.test.ts @@ -32,8 +32,8 @@ describe("https://github.com/neo4j/graphql/issues/2925", () => { type User @node { name: String - hasGroup: Group @relationship(type: "HAS_GROUP", direction: OUT) - hasRequiredGroup: Group! @relationship(type: "HAS_REQUIRED_GROUP", direction: OUT) + hasGroup: [Group!]! @relationship(type: "HAS_GROUP", direction: OUT) + hasRequiredGroup: [Group!]! @relationship(type: "HAS_REQUIRED_GROUP", direction: OUT) } `; @@ -46,7 +46,7 @@ describe("https://github.com/neo4j/graphql/issues/2925", () => { test("should query relationship", async () => { const query = /* GraphQL */ ` query Query { - users(where: { hasGroup: { name_IN: ["Group A"] } }) { + users(where: { hasGroup: { some: { name: { in: ["Group A"] } } } }) { name } } @@ -55,37 +55,12 @@ describe("https://github.com/neo4j/graphql/issues/2925", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE single(this0 IN [(this)-[:HAS_GROUP]->(this0:Group) WHERE this0.name IN $param0 | 1] WHERE true) - RETURN this { .name } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": [ - \\"Group A\\" - ] - }" - `); - }); - - test("should query required relationship", async () => { - const query = /* GraphQL */ ` - query Query { - users(where: { hasRequiredGroup: { name_IN: ["Group A"] } }) { - name - } + "CYPHER 5 + MATCH (this:User) + WHERE EXISTS { + MATCH (this)-[:HAS_GROUP]->(this0:Group) + WHERE this0.name IN $param0 } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - OPTIONAL MATCH (this)-[:HAS_REQUIRED_GROUP]->(this0:Group) - WITH *, count(this0) AS var1 - WITH * - WHERE (var1 <> 0 AND this0.name IN $param0) RETURN this { .name } AS this" `); @@ -101,7 +76,7 @@ describe("https://github.com/neo4j/graphql/issues/2925", () => { test("should query nested relationship", async () => { const query = /* GraphQL */ ` query Query { - groups(where: { hasGroupUser_SOME: { hasGroup: { name_IN: ["Group A"] } } }) { + groups(where: { hasGroupUser: { some: { hasGroup: { some: { name: { in: ["Group A"] } } } } } }) { name } } @@ -110,47 +85,15 @@ describe("https://github.com/neo4j/graphql/issues/2925", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Group) + "CYPHER 5 + MATCH (this:Group) WHERE EXISTS { MATCH (this)<-[:HAS_GROUP]-(this0:User) - WHERE single(this1 IN [(this0)-[:HAS_GROUP]->(this1:Group) WHERE this1.name IN $param0 | 1] WHERE true) - } - RETURN this { .name } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": [ - \\"Group A\\" - ] - }" - `); - }); - - test("should query nested required relationship", async () => { - const query = /* GraphQL */ ` - query Query { - groups(where: { hasGroupUser_SOME: { hasRequiredGroup: { name_IN: ["Group A"] } } }) { - name + WHERE EXISTS { + MATCH (this0)-[:HAS_GROUP]->(this1:Group) + WHERE this1.name IN $param0 } } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Group) - CALL { - WITH this - MATCH (this)<-[:HAS_GROUP]-(this0:User) - OPTIONAL MATCH (this0)-[:HAS_REQUIRED_GROUP]->(this1:Group) - WITH *, count(this1) AS var2 - WITH * - WHERE (var2 <> 0 AND this1.name IN $param0) - RETURN count(this0) > 0 AS var3 - } - WITH * - WHERE var3 = true RETURN this { .name } AS this" `); diff --git a/packages/graphql/tests/tck/issues/3027.test.ts b/packages/graphql/tests/tck/issues/3027.test.ts deleted file mode 100644 index b8618f081d..0000000000 --- a/packages/graphql/tests/tck/issues/3027.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/3027", () => { - describe("union", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = /* GraphQL */ ` - type Book @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - union BookTitle = BookTitle_SV | BookTitle_EN - - type BookTitle_SV @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type BookTitle_EN @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should validate cardinality against all the implementations", async () => { - const query = /* GraphQL */ ` - mutation UpdateBooks { - updateBooks( - where: { isbn_EQ: "123" } - update: { - translatedTitle: { BookTitle_EN: { create: { node: { value: "English book title" } } } } - } - ) { - info { - nodesCreated - nodesDeleted - relationshipsCreated - relationshipsDeleted - } - books { - isbn - originalTitle - translatedTitle { - ... on BookTitle_SV { - value - } - ... on BookTitle_EN { - value - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Book) - WHERE this.isbn = $param0 - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)<-[:TRANSLATED_BOOK_TITLE]-(:BookTitle_SV)) OR EXISTS((this)<-[:TRANSLATED_BOOK_TITLE]-(:BookTitle_EN)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"Book\\",\\"translatedTitle\\"]) - CREATE (this_translatedTitle_BookTitle_EN0_create0_node:BookTitle_EN) - SET this_translatedTitle_BookTitle_EN0_create0_node.value = $this_translatedTitle_BookTitle_EN0_create0_node_value - MERGE (this)<-[:TRANSLATED_BOOK_TITLE]-(this_translatedTitle_BookTitle_EN0_create0_node) - WITH this, this_translatedTitle_BookTitle_EN0_create0_node - CALL { - WITH this_translatedTitle_BookTitle_EN0_create0_node - MATCH (this_translatedTitle_BookTitle_EN0_create0_node)-[this_translatedTitle_BookTitle_EN0_create0_node_book_Book_unique:TRANSLATED_BOOK_TITLE]->(:Book) - WITH count(this_translatedTitle_BookTitle_EN0_create0_node_book_Book_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDBookTitle_EN.book required exactly once', [0]) - RETURN c AS this_translatedTitle_BookTitle_EN0_create0_node_book_Book_unique_ignored - } - WITH * - CALL { - WITH this - CALL { - WITH * - MATCH (this)<-[update_this0:TRANSLATED_BOOK_TITLE]-(update_this1:BookTitle_SV) - WITH update_this1 { .value, __resolveType: \\"BookTitle_SV\\", __id: id(update_this1) } AS update_this1 - RETURN update_this1 AS update_var2 - UNION - WITH * - MATCH (this)<-[update_this3:TRANSLATED_BOOK_TITLE]-(update_this4:BookTitle_EN) - WITH update_this4 { .value, __resolveType: \\"BookTitle_EN\\", __id: id(update_this4) } AS update_this4 - RETURN update_this4 AS update_var2 - } - WITH update_var2 - RETURN head(collect(update_var2)) AS update_var2 - } - RETURN collect(DISTINCT this { .isbn, .originalTitle, translatedTitle: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"123\\", - \\"this_translatedTitle_BookTitle_EN0_create0_node_value\\": \\"English book title\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("interface", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = /* GraphQL */ ` - type Book @node { - originalTitle: String! - translatedTitle: BookTitle @relationship(type: "TRANSLATED_BOOK_TITLE", direction: IN) - isbn: String! - } - - interface BookTitle { - value: String! - } - - type BookTitle_SV implements BookTitle @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - - type BookTitle_EN implements BookTitle @node { - book: Book! @relationship(type: "TRANSLATED_BOOK_TITLE", direction: OUT) - value: String! - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should validate cardinality against all the implementations", async () => { - const query = /* GraphQL */ ` - mutation UpdateBooks { - updateBooks( - where: { isbn_EQ: "123" } - update: { - translatedTitle: { create: { node: { BookTitle_EN: { value: "English book title" } } } } - } - ) { - books { - isbn - originalTitle - translatedTitle { - ... on BookTitle_SV { - value - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Book) - WHERE this.isbn = $param0 - WITH this - CALL { - WITH this - WITH this - RETURN count(*) AS update_this_BookTitle_SV - } - CALL { - WITH this - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)<-[:TRANSLATED_BOOK_TITLE]-(:BookTitle_SV)) OR EXISTS((this)<-[:TRANSLATED_BOOK_TITLE]-(:BookTitle_EN)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"Book\\",\\"translatedTitle\\"]) - CREATE (this_translatedTitle0_create0_node:BookTitle_EN) - SET this_translatedTitle0_create0_node.value = $this_translatedTitle0_create0_node_value - MERGE (this)<-[:TRANSLATED_BOOK_TITLE]-(this_translatedTitle0_create0_node) - WITH this, this_translatedTitle0_create0_node - CALL { - WITH this_translatedTitle0_create0_node - MATCH (this_translatedTitle0_create0_node)-[this_translatedTitle0_create0_node_book_Book_unique:TRANSLATED_BOOK_TITLE]->(:Book) - WITH count(this_translatedTitle0_create0_node_book_Book_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDBookTitle_EN.book required exactly once', [0]) - RETURN c AS this_translatedTitle0_create0_node_book_Book_unique_ignored - } - RETURN count(*) AS update_this_BookTitle_EN - } - WITH * - CALL { - WITH this - CALL { - WITH * - MATCH (this)<-[update_this0:TRANSLATED_BOOK_TITLE]-(update_this1:BookTitle_SV) - WITH update_this1 { .value, __resolveType: \\"BookTitle_SV\\", __id: id(update_this1) } AS update_this1 - RETURN update_this1 AS update_var2 - UNION - WITH * - MATCH (this)<-[update_this3:TRANSLATED_BOOK_TITLE]-(update_this4:BookTitle_EN) - WITH update_this4 { __resolveType: \\"BookTitle_EN\\", __id: id(update_this4) } AS update_this4 - RETURN update_this4 AS update_var2 - } - WITH update_var2 - RETURN head(collect(update_var2)) AS update_var2 - } - RETURN collect(DISTINCT this { .isbn, .originalTitle, translatedTitle: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"123\\", - \\"this_translatedTitle0_create0_node_value\\": \\"English book title\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/issues/3215.test.ts b/packages/graphql/tests/tck/issues/3215.test.ts index 8675530413..19dc3c0ce4 100644 --- a/packages/graphql/tests/tck/issues/3215.test.ts +++ b/packages/graphql/tests/tck/issues/3215.test.ts @@ -40,7 +40,7 @@ describe("https://github.com/neo4j/graphql/issues/3215", () => { test("should ignore undefined parameters on NOT fields", async () => { const query = /* GraphQL */ ` query MyQuery($name: String) { - actors(where: { age_GT: 25, NOT: { name_EQ: $name } }) { + actors(where: { age: { gt: 25 }, NOT: { name: { eq: $name } } }) { name age } @@ -50,7 +50,8 @@ describe("https://github.com/neo4j/graphql/issues/3215", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.age > $param0 RETURN this { .name, .age } AS this" `); @@ -68,7 +69,7 @@ describe("https://github.com/neo4j/graphql/issues/3215", () => { test("should ignore undefined parameters on boolean NOT", async () => { const query = /* GraphQL */ ` query MyQuery($name: String) { - actors(where: { age_GT: 25, NOT: { name: $name } }) { + actors(where: { age: { gt: 25 }, NOT: { name: { eq: $name } } }) { name age } @@ -78,7 +79,8 @@ describe("https://github.com/neo4j/graphql/issues/3215", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.age > $param0 RETURN this { .name, .age } AS this" `); diff --git a/packages/graphql/tests/tck/issues/324.test.ts b/packages/graphql/tests/tck/issues/324.test.ts deleted file mode 100644 index b3fc54241a..0000000000 --- a/packages/graphql/tests/tck/issues/324.test.ts +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("#324", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Person @node { - identifier: ID! - car: Car! @relationship(type: "CAR", direction: OUT) - } - - type Car @node { - identifier: ID! - manufacturer: Manufacturer! @relationship(type: "MANUFACTURER", direction: OUT) - } - - type Manufacturer @node { - identifier: ID! - logo: Logo! @relationship(type: "LOGO", direction: OUT) - name: String - } - - type Logo @node { - identifier: ID! - name: String - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Should have correct variables in apoc.do.when", async () => { - const query = /* GraphQL */ ` - mutation updatePeople($where: PersonWhere, $update: PersonUpdateInput) { - updatePeople(where: $where, update: $update) { - people { - identifier - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - variableValues: { - where: { identifier_EQ: "Someone" }, - update: { - car: { - update: { - node: { - manufacturer: { - update: { - node: { - name_SET: "Manufacturer", - logo: { connect: { where: { node: { identifier_EQ: "Opel Logo" } } } }, - }, - }, - }, - }, - }, - }, - }, - }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Person) - WHERE this.identifier = $param0 - WITH this - CALL { - WITH this - MATCH (this)-[this_car0_relationship:CAR]->(this_car0:Car) - WITH this, this_car0 - CALL { - WITH this, this_car0 - MATCH (this_car0)-[this_car0_manufacturer0_relationship:MANUFACTURER]->(this_car0_manufacturer0:Manufacturer) - SET this_car0_manufacturer0.name = $this_update_car0_manufacturer0_name_SET - WITH * - CALL { - WITH this, this_car0, this_car0_manufacturer0 - OPTIONAL MATCH (this_car0_manufacturer0_logo0_connect0_node:Logo) - WHERE this_car0_manufacturer0_logo0_connect0_node.identifier = $this_car0_manufacturer0_logo0_connect0_node_param0 - CALL { - WITH * - WITH this, this_car0, collect(this_car0_manufacturer0_logo0_connect0_node) as connectedNodes, collect(this_car0_manufacturer0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this_car0_manufacturer0 - UNWIND connectedNodes as this_car0_manufacturer0_logo0_connect0_node - MERGE (this_car0_manufacturer0)-[:LOGO]->(this_car0_manufacturer0_logo0_connect0_node) - } - } - WITH this, this_car0, this_car0_manufacturer0, this_car0_manufacturer0_logo0_connect0_node - RETURN count(*) AS connect_this_car0_manufacturer0_logo0_connect_Logo0 - } - WITH this, this_car0, this_car0_manufacturer0 - CALL { - WITH this_car0_manufacturer0 - MATCH (this_car0_manufacturer0)-[this_car0_manufacturer0_logo_Logo_unique:LOGO]->(:Logo) - WITH count(this_car0_manufacturer0_logo_Logo_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDManufacturer.logo required exactly once', [0]) - RETURN c AS this_car0_manufacturer0_logo_Logo_unique_ignored - } - RETURN count(*) AS update_this_car0_manufacturer0 - } - WITH this, this_car0 - CALL { - WITH this_car0 - MATCH (this_car0)-[this_car0_manufacturer_Manufacturer_unique:MANUFACTURER]->(:Manufacturer) - WITH count(this_car0_manufacturer_Manufacturer_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDCar.manufacturer required exactly once', [0]) - RETURN c AS this_car0_manufacturer_Manufacturer_unique_ignored - } - RETURN count(*) AS update_this_car0 - } - WITH * - CALL { - WITH this - MATCH (this)-[this_car_Car_unique:CAR]->(:Car) - WITH count(this_car_Car_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPerson.car required exactly once', [0]) - RETURN c AS this_car_Car_unique_ignored - } - RETURN collect(DISTINCT this { .identifier }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"Someone\\", - \\"this_update_car0_manufacturer0_name_SET\\": \\"Manufacturer\\", - \\"this_car0_manufacturer0_logo0_connect0_node_param0\\": \\"Opel Logo\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/3251.test.ts b/packages/graphql/tests/tck/issues/3251.test.ts deleted file mode 100644 index d131cec551..0000000000 --- a/packages/graphql/tests/tck/issues/3251.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/3251", () => { - describe("1:1 schema validation", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = `#graphql - type Movie @node { - name: String! - genre: Genre! @relationship(type: "HAS_GENRE", direction: OUT) - } - - type Genre @node { - name: String! @unique - movies: [Movie!]! @relationship(type: "HAS_GENRE", direction: IN) - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should have check in correct place following update and connect", async () => { - const query = /* GraphQL */ ` - mutation UpdateMovieWithConnectAndUpdate { - updateMovies( - where: { name_EQ: "TestMovie1" } - update: { - name_SET: "TestMovie1" - genre: { connect: { where: { node: { name_EQ: "Thriller" } } } } - } - ) { - movies { - name - genre { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.name = $param0 - SET this.name = $this_update_name_SET - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_genre0_connect0_node:Genre) - WHERE this_genre0_connect0_node.name = $this_genre0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_genre0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_genre0_connect0_node - MERGE (this)-[:HAS_GENRE]->(this_genre0_connect0_node) - } - } - WITH this, this_genre0_connect0_node - RETURN count(*) AS connect_this_genre0_connect_Genre0 - } - WITH * - WITH * - CALL { - WITH this - MATCH (this)-[this_genre_Genre_unique:HAS_GENRE]->(:Genre) - WITH count(this_genre_Genre_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.genre required exactly once', [0]) - RETURN c AS this_genre_Genre_unique_ignored - } - CALL { - WITH this - MATCH (this)-[update_this0:HAS_GENRE]->(update_this1:Genre) - WITH update_this1 { .name } AS update_this1 - RETURN head(collect(update_this1)) AS update_var2 - } - RETURN collect(DISTINCT this { .name, genre: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"TestMovie1\\", - \\"this_update_name_SET\\": \\"TestMovie1\\", - \\"this_genre0_connect0_node_param0\\": \\"Thriller\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); -}); diff --git a/packages/graphql/tests/tck/issues/3394.test.ts b/packages/graphql/tests/tck/issues/3394.test.ts index 8e9d175f66..a4aa2c061e 100644 --- a/packages/graphql/tests/tck/issues/3394.test.ts +++ b/packages/graphql/tests/tck/issues/3394.test.ts @@ -55,7 +55,8 @@ describe("https://github.com/neo4j/graphql/issues/3394", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Product) + "CYPHER 5 + MATCH (this:Product) WITH * ORDER BY this.fg_item DESC RETURN this { .description, id: this.fg_item_id, partNumber: this.fg_item } AS this" @@ -80,10 +81,12 @@ describe("https://github.com/neo4j/graphql/issues/3394", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Employee) + "CYPHER 5 + MATCH (this:Employee) CALL { WITH this MATCH (this)-[this0:CAN_ACCESS]->(this1:Product) + WITH DISTINCT this1 WITH this1 { .description, id: this1.fg_item_id, partNumber: this1.fg_item } AS this1 ORDER BY this1.partNumber DESC RETURN collect(this1) AS var2 @@ -112,7 +115,8 @@ describe("https://github.com/neo4j/graphql/issues/3394", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Product) + "CYPHER 5 + MATCH (this0:Product) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -149,7 +153,8 @@ describe("https://github.com/neo4j/graphql/issues/3394", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Employee) + "CYPHER 5 + MATCH (this:Employee) CALL { WITH this MATCH (this)-[this0:CAN_ACCESS]->(this1:Product) diff --git a/packages/graphql/tests/tck/issues/360.test.ts b/packages/graphql/tests/tck/issues/360.test.ts index 223db53082..af588e6967 100644 --- a/packages/graphql/tests/tck/issues/360.test.ts +++ b/packages/graphql/tests/tck/issues/360.test.ts @@ -44,7 +44,13 @@ describe("#360", () => { const query = /* GraphQL */ ` query ($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) { events( - where: { AND: [{ start_GTE: $rangeStart }, { start_LTE: $rangeEnd }, { activity_EQ: $activity }] } + where: { + AND: [ + { start: { gte: $rangeStart } } + { start: { lte: $rangeEnd } } + { activity: { eq: $activity } } + ] + } ) { start activity @@ -57,33 +63,16 @@ describe("#360", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Event) - WHERE (this.start >= $param0 AND this.start <= $param1) + "CYPHER 5 + MATCH (this:Event) + WHERE (this.start >= datetime($param0) AND this.start <= datetime($param1)) RETURN this { .activity, start: apoc.date.convertFormat(toString(this.start), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 17, - \\"hour\\": 23, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"param1\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 18, - \\"hour\\": 22, - \\"minute\\": 59, - \\"second\\": 59, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-07-18T00:00:00+0100\\", + \\"param1\\": \\"2021-07-18T23:59:59+0100\\" }" `); }); @@ -91,7 +80,15 @@ describe("#360", () => { test("Should exclude undefined members in OR", async () => { const query = /* GraphQL */ ` query ($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) { - events(where: { OR: [{ start_GTE: $rangeStart }, { start_LTE: $rangeEnd }, { activity_EQ: $activity }] }) { + events( + where: { + OR: [ + { start: { gte: $rangeStart } } + { start: { lte: $rangeEnd } } + { activity: { eq: $activity } } + ] + } + ) { start activity } @@ -103,33 +100,16 @@ describe("#360", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Event) - WHERE (this.start >= $param0 OR this.start <= $param1) + "CYPHER 5 + MATCH (this:Event) + WHERE (this.start >= datetime($param0) OR this.start <= datetime($param1)) RETURN this { .activity, start: apoc.date.convertFormat(toString(this.start), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 17, - \\"hour\\": 23, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"param1\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 18, - \\"hour\\": 22, - \\"minute\\": 59, - \\"second\\": 59, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-07-18T00:00:00+0100\\", + \\"param1\\": \\"2021-07-18T23:59:59+0100\\" }" `); }); diff --git a/packages/graphql/tests/tck/issues/3765.test.ts b/packages/graphql/tests/tck/issues/3765.test.ts index ba2e2dd335..c9cfe7c120 100644 --- a/packages/graphql/tests/tck/issues/3765.test.ts +++ b/packages/graphql/tests/tck/issues/3765.test.ts @@ -50,7 +50,14 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("filter + explicit AND", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_GT: 10, AND: [{ count_GT: 25 }, { count_LT: 33 }] } }) { + posts( + where: { + likesAggregate: { + count: { gt: 10 } + AND: [{ count: { gt: 25 } }, { count: { lt: 33 } }] + } + } + ) { content } } @@ -59,16 +66,17 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - CALL { - WITH this - MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND (count(this1) > $param1 AND count(this1) < $param2)) AS var2 - } - WITH * - WHERE var2 = true - RETURN this { .content } AS this" - `); + "CYPHER 5 + MATCH (this:Post) + CALL { + WITH this + MATCH (this)<-[this0:LIKES]-(this1:User) + RETURN (count(this1) > $param0 AND (count(this1) > $param1 AND count(this1) < $param2)) AS var2 + } + WITH * + WHERE var2 = true + RETURN this { .content } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -91,7 +99,7 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("filter + implicit AND", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_GT: 10, AND: [{ count_GT: 25, count_LT: 33 }] } }) { + posts(where: { likesAggregate: { count: { gt: 10 }, AND: [{ count: { gt: 25, lt: 33 } }] } }) { content } } @@ -100,39 +108,47 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - CALL { - WITH this - MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND (count(this1) < $param1 AND count(this1) > $param2)) AS var2 - } - WITH * - WHERE var2 = true - RETURN this { .content } AS this" - `); + "CYPHER 5 + MATCH (this:Post) + CALL { + WITH this + MATCH (this)<-[this0:LIKES]-(this1:User) + RETURN (count(this1) > $param0 AND (count(this1) > $param1 AND count(this1) < $param2)) AS var2 + } + WITH * + WHERE var2 = true + RETURN this { .content } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": { - \\"low\\": 10, - \\"high\\": 0 - }, - \\"param1\\": { - \\"low\\": 33, - \\"high\\": 0 - }, - \\"param2\\": { - \\"low\\": 25, - \\"high\\": 0 - } - }" - `); + "{ + \\"param0\\": { + \\"low\\": 10, + \\"high\\": 0 + }, + \\"param1\\": { + \\"low\\": 25, + \\"high\\": 0 + }, + \\"param2\\": { + \\"low\\": 33, + \\"high\\": 0 + } + }" + `); }); test("filter + explicit OR", async () => { const query = /* GraphQL */ ` { - posts(where: { likesAggregate: { count_GT: 10, OR: [{ count_GT: 25 }, { count_LT: 33 }] } }) { + posts( + where: { + likesAggregate: { + count: { gt: 10 } + OR: [{ count: { gt: 25 } }, { count: { lt: 33 } }] + } + } + ) { content } } @@ -141,16 +157,17 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - CALL { - WITH this - MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND (count(this1) > $param1 OR count(this1) < $param2)) AS var2 - } - WITH * - WHERE var2 = true - RETURN this { .content } AS this" - `); + "CYPHER 5 + MATCH (this:Post) + CALL { + WITH this + MATCH (this)<-[this0:LIKES]-(this1:User) + RETURN (count(this1) > $param0 AND (count(this1) > $param1 OR count(this1) < $param2)) AS var2 + } + WITH * + WHERE var2 = true + RETURN this { .content } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -176,8 +193,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 - OR: [{ count_GT: 25, count_LTE: 99 }, { count_LT: 33 }] + count: { gt: 10 } + OR: [{ count: { gt: 25, lte: 99 } }, { count: { lt: 33 } }] } } ) { @@ -189,37 +206,38 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - CALL { - WITH this - MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND ((count(this1) <= $param1 AND count(this1) > $param2) OR count(this1) < $param3)) AS var2 - } - WITH * - WHERE var2 = true - RETURN this { .content } AS this" - `); + "CYPHER 5 + MATCH (this:Post) + CALL { + WITH this + MATCH (this)<-[this0:LIKES]-(this1:User) + RETURN (count(this1) > $param0 AND ((count(this1) > $param1 AND count(this1) <= $param2) OR count(this1) < $param3)) AS var2 + } + WITH * + WHERE var2 = true + RETURN this { .content } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": { - \\"low\\": 10, - \\"high\\": 0 - }, - \\"param1\\": { - \\"low\\": 99, - \\"high\\": 0 - }, - \\"param2\\": { - \\"low\\": 25, - \\"high\\": 0 - }, - \\"param3\\": { - \\"low\\": 33, - \\"high\\": 0 - } - }" - `); + "{ + \\"param0\\": { + \\"low\\": 10, + \\"high\\": 0 + }, + \\"param1\\": { + \\"low\\": 25, + \\"high\\": 0 + }, + \\"param2\\": { + \\"low\\": 99, + \\"high\\": 0 + }, + \\"param3\\": { + \\"low\\": 33, + \\"high\\": 0 + } + }" + `); }); test("filter + explicit OR which contains an explicit AND", async () => { @@ -228,8 +246,11 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 - OR: [{ AND: [{ count_GT: 25 }, { count_LTE: 99 }] }, { count_LT: 33 }] + count: { gt: 10 } + OR: [ + { AND: [{ count: { gt: 25 } }, { count: { lte: 99 } }] } + { count: { lt: 33 } } + ] } } ) { @@ -241,16 +262,17 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) - CALL { - WITH this - MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND ((count(this1) > $param1 AND count(this1) <= $param2) OR count(this1) < $param3)) AS var2 - } - WITH * - WHERE var2 = true - RETURN this { .content } AS this" - `); + "CYPHER 5 + MATCH (this:Post) + CALL { + WITH this + MATCH (this)<-[this0:LIKES]-(this1:User) + RETURN (count(this1) > $param0 AND ((count(this1) > $param1 AND count(this1) <= $param2) OR count(this1) < $param3)) AS var2 + } + WITH * + WHERE var2 = true + RETURN this { .content } AS this" + `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ @@ -282,8 +304,13 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 - node: { AND: [{ name_SHORTEST_LENGTH_GT: 25 }, { name_SHORTEST_LENGTH_LT: 80 }] } + count: { gt: 10 } + node: { + AND: [ + { name: { shortestLength: { gt: 25 } } } + { name: { shortestLength: { lt: 80 } } } + ] + } } } ) { @@ -295,7 +322,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -330,8 +358,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 - node: { AND: [{ name_SHORTEST_LENGTH_GT: 25, name_SHORTEST_LENGTH_LT: 80 }] } + count: { gt: 10 } + node: { AND: [{ name: { shortestLength: { gt: 25, lt: 80 } } }] } } } ) { @@ -343,7 +371,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -378,8 +407,13 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 - node: { OR: [{ name_SHORTEST_LENGTH_GT: 25 }, { name_SHORTEST_LENGTH_LT: 80 }] } + count: { gt: 10 } + node: { + OR: [ + { name: { shortestLength: { gt: 25 } } } + { name: { shortestLength: { lt: 80 } } } + ] + } } } ) { @@ -391,7 +425,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -426,11 +461,11 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 + count: { gt: 10 } node: { OR: [ - { name_SHORTEST_LENGTH_GT: 25, name_SHORTEST_LENGTH_LT: 40 } - { name_SHORTEST_LENGTH_GTE: 1233 } + { name: { shortestLength: { gt: 25, lt: 40 } } } + { name: { shortestLength: { gte: 1233 } } } ] } } @@ -444,7 +479,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) @@ -483,15 +519,15 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { likesAggregate: { - count_GT: 10 + count: { gt: 10 } OR: [ { - edge: { someProp_LONGEST_LENGTH_GT: 4, someProp_SHORTEST_LENGTH_LT: 10 } - node: { name_AVERAGE_LENGTH_GT: 3782 } + edge: { someProp: { shortestLength: { lt: 10 }, longestLength: { gt: 4 } } } + node: { name: { averageLength: { gt: 3782 } } } } - { node: { name_SHORTEST_LENGTH_GT: 25 } } + { node: { name: { shortestLength: { gt: 25 } } } } ] - edge: { someProp_LONGEST_LENGTH_LT: 12, someProp_SHORTEST_LENGTH_GT: 20 } + edge: { someProp: { longestLength: { lt: 12 }, shortestLength: { gt: 20 } } } } } ) { @@ -503,11 +539,12 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN (count(this1) > $param0 AND ((avg(size(this1.name)) > $param1 AND (max(size(this0.someProp)) > $param2 AND min(size(this0.someProp)) < $param3)) OR min(size(this1.name)) > $param4) AND (min(size(this0.someProp)) > $param5 AND max(size(this0.someProp)) < $param6)) AS var2 + RETURN (count(this1) > $param0 AND ((avg(size(this1.name)) > $param1 AND (min(size(this0.someProp)) < $param2 AND max(size(this0.someProp)) > $param3)) OR min(size(this1.name)) > $param4) AND (min(size(this0.someProp)) > $param5 AND max(size(this0.someProp)) < $param6)) AS var2 } WITH * WHERE var2 = true @@ -522,11 +559,11 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { }, \\"param1\\": 3782, \\"param2\\": { - \\"low\\": 4, + \\"low\\": 10, \\"high\\": 0 }, \\"param3\\": { - \\"low\\": 10, + \\"low\\": 4, \\"high\\": 0 }, \\"param4\\": { @@ -551,7 +588,7 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("implicit AND", async () => { const query = /* GraphQL */ ` { - posts(where: { content_EQ: "stuff", alternateContent_EQ: "stuff2" }) { + posts(where: { content: { eq: "stuff" }, alternateContent: { eq: "stuff2" } }) { content } } @@ -560,7 +597,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE (this.content = $param0 AND this.alternateContent = $param1) RETURN this { .content } AS this" `); @@ -578,7 +616,10 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { { posts( where: { - OR: [{ content_EQ: "stuff", alternateContent_EQ: "stuff2" }, { content_EQ: "stuff3" }] + OR: [ + { content: { eq: "stuff" }, alternateContent: { eq: "stuff2" } } + { content: { eq: "stuff3" } } + ] } ) { content @@ -589,7 +630,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE ((this.content = $param0 AND this.alternateContent = $param1) OR this.content = $param2) RETURN this { .content } AS this" `); @@ -606,7 +648,7 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("explicit NOT with an implicit AND", async () => { const query = /* GraphQL */ ` { - posts(where: { NOT: { content_EQ: "stuff", alternateContent_EQ: "stuff2" } }) { + posts(where: { NOT: { content: { eq: "stuff" }, alternateContent: { eq: "stuff2" } } }) { content } } @@ -615,7 +657,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE NOT (this.content = $param0 AND this.alternateContent = $param1) RETURN this { .content } AS this" `); @@ -633,7 +676,7 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("implicit AND inside relationship filter", async () => { const query = /* GraphQL */ ` { - posts(where: { likes_SOME: { name_EQ: "stuff", otherName_EQ: "stuff2" } }) { + posts(where: { likes: { some: { name: { eq: "stuff" }, otherName: { eq: "stuff2" } } } }) { content } } @@ -642,7 +685,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE EXISTS { MATCH (this)<-[:LIKES]-(this0:User) WHERE (this0.name = $param0 AND this0.otherName = $param1) @@ -661,7 +705,7 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { test("implicit AND outside relationship filters", async () => { const query = /* GraphQL */ ` { - posts(where: { likes_SOME: { name_EQ: "stuff" }, likes_ALL: { otherName_EQ: "stuff2" } }) { + posts(where: { likes: { some: { name: { eq: "stuff" } }, all: { otherName: { eq: "stuff2" } } } }) { content } } @@ -670,7 +714,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE ((EXISTS { MATCH (this)<-[:LIKES]-(this0:User) WHERE this0.otherName = $param0 @@ -698,9 +743,9 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { posts( where: { OR: [ - { likes_SOME: { name_EQ: "stuff" } } - { likes_ALL: { otherName_EQ: "stuff2" } } - { likes_SOME: { otherName_EQ: "stuff3" } } + { likes: { some: { name: { eq: "stuff" } } } } + { likes: { all: { otherName: { eq: "stuff2" } } } } + { likes: { some: { otherName: { eq: "stuff3" } } } } ] } ) { @@ -712,7 +757,8 @@ describe("https://github.com/neo4j/graphql/issues/3765", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Post) + "CYPHER 5 + MATCH (this:Post) WHERE (EXISTS { MATCH (this)<-[:LIKES]-(this0:User) WHERE this0.name = $param0 diff --git a/packages/graphql/tests/tck/issues/387.test.ts b/packages/graphql/tests/tck/issues/387.test.ts index f84a5bf38f..d713d0dabf 100644 --- a/packages/graphql/tests/tck/issues/387.test.ts +++ b/packages/graphql/tests/tck/issues/387.test.ts @@ -81,7 +81,8 @@ describe("https://github.com/neo4j/graphql/issues/387", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Place) + "CYPHER 5 + MATCH (this:Place) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/3888.test.ts b/packages/graphql/tests/tck/issues/3888.test.ts deleted file mode 100644 index 7de1a7c9ed..0000000000 --- a/packages/graphql/tests/tck/issues/3888.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/3888", () => { - const secret = "secret"; - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type User @node { - id: ID! - } - - type Post @authorization(filter: [{ where: { node: { author: { id_EQ: "$jwt.sub" } } } }]) @node { - title: String! - content: String! - author: User! @relationship(type: "AUTHORED", direction: IN) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("should not add an authorization check for connects coming from create", async () => { - const query = /* GraphQL */ ` - mutation { - createPosts( - input: [ - { title: "Test1", content: "Test1", author: { connect: { where: { node: { id_EQ: "michel" } } } } } - ] - ) { - posts { - title - } - } - } - `; - - const token = createBearerToken(secret, { sub: "michel" }); - const result = await translateQuery(neoSchema, query, { - token, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Post) - SET this0.title = $this0_title - SET this0.content = $this0_content - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_author_connect0_node:User) - WHERE this0_author_connect0_node.id = $this0_author_connect0_node_param0 - CALL { - WITH * - WITH collect(this0_author_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_author_connect0_node - MERGE (this0)<-[:AUTHORED]-(this0_author_connect0_node) - } - } - WITH this0, this0_author_connect0_node - RETURN count(*) AS connect_this0_author_connect_User0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_author_User_unique:AUTHORED]-(:User) - WITH count(this0_author_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPost.author required exactly once', [0]) - RETURN c AS this0_author_User_unique_ignored - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Test1\\", - \\"this0_content\\": \\"Test1\\", - \\"this0_author_connect0_node_param0\\": \\"michel\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/3901.test.ts b/packages/graphql/tests/tck/issues/3901.test.ts deleted file mode 100644 index 12b7145181..0000000000 --- a/packages/graphql/tests/tck/issues/3901.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/3901", () => { - const secret = "secret"; - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type User @node { - id: ID! @id - series: [Serie!]! @relationship(type: "PUBLISHER", direction: OUT) - } - - type Serie - @node - @authorization( - validate: [ - { - operations: [CREATE] - when: [AFTER] - where: { - AND: [ - { node: { publisher: { id_EQ: "$jwt.sub" } } } - { jwt: { roles_INCLUDES: "verified" } } - { jwt: { roles_INCLUDES: "creator" } } - ] - } - } - ] - ) { - id: ID! @id - title: String! - - seasons: [Season!]! @relationship(type: "SEASON_OF", direction: IN) - publisher: User! @relationship(type: "PUBLISHER", direction: IN) - } - - type Season - @node - @authorization( - validate: [ - { - operations: [CREATE] - when: [AFTER] - where: { - AND: [ - { node: { serie: { publisher: { id_EQ: "$jwt.sub" } } } } - { jwt: { roles_INCLUDES: "verified" } } - { jwt: { roles_INCLUDES: "creator" } } - ] - } - } - ] - ) { - id: ID! @id - number: Int! - serie: Serie! @relationship(type: "SEASON_OF", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("should not add an authorization check for connects coming from create", async () => { - const query = /* GraphQL */ ` - mutation createSerie($title: String!, $userId: ID!) { - createSeries( - input: [ - { - title: $title - publisher: { connect: { where: { node: { id_EQ: $userId } } } } - seasons: { create: { node: { number: 1 } } } - } - ] - ) { - series { - id - title - } - } - } - `; - - const token = createBearerToken(secret, { sub: "michel" }); - const result = await translateQuery(neoSchema, query, { - variableValues: { title: "Title", userId: "ID" }, - token, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Serie) - SET this0.id = randomUUID() - SET this0.title = $this0_title - WITH * - CREATE (this0_seasons0_node:Season) - SET this0_seasons0_node.id = randomUUID() - SET this0_seasons0_node.number = $this0_seasons0_node_number - MERGE (this0)<-[:SEASON_OF]-(this0_seasons0_node) - WITH * - CALL { - WITH this0_seasons0_node - MATCH (this0_seasons0_node)-[this0_seasons0_node_serie_Serie_unique:SEASON_OF]->(:Serie) - WITH count(this0_seasons0_node_serie_Serie_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSeason.serie required exactly once', [0]) - RETURN c AS this0_seasons0_node_serie_Serie_unique_ignored - } - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_publisher_connect0_node:User) - WHERE this0_publisher_connect0_node.id = $this0_publisher_connect0_node_param0 - CALL { - WITH * - WITH collect(this0_publisher_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_publisher_connect0_node - MERGE (this0)<-[:PUBLISHER]-(this0_publisher_connect0_node) - } - } - WITH this0, this0_publisher_connect0_node - RETURN count(*) AS connect_this0_publisher_connect_User0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_publisher_User_unique:PUBLISHER]-(:User) - WITH count(this0_publisher_User_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSerie.publisher required exactly once', [0]) - RETURN c AS this0_publisher_User_unique_ignored - } - WITH * - CALL { - WITH this0_seasons0_node - MATCH (this0_seasons0_node)-[:SEASON_OF]->(authorization_0_1_0_0_after_this1:Serie) - OPTIONAL MATCH (authorization_0_1_0_0_after_this1)<-[:PUBLISHER]-(authorization_0_1_0_0_after_this2:User) - WITH *, count(authorization_0_1_0_0_after_this2) AS authorization_0_1_0_0_after_var3 - WITH * - WHERE (authorization_0_1_0_0_after_var3 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_0_1_0_0_after_this2.id = $jwt.sub)) - RETURN count(authorization_0_1_0_0_after_this1) = 1 AS authorization_0_1_0_0_after_var0 - } - OPTIONAL MATCH (this0)<-[:PUBLISHER]-(authorization_0_after_this1:User) - WITH *, count(authorization_0_after_this1) AS authorization_0_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_1_0_0_after_var0 = true AND ($jwt.roles IS NOT NULL AND $authorization_0_1_0_0_after_param2 IN $jwt.roles) AND ($jwt.roles IS NOT NULL AND $authorization_0_1_0_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((authorization_0_after_var0 <> 0 AND ($jwt.sub IS NOT NULL AND authorization_0_after_this1.id = $jwt.sub)) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles) AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .id, .title } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_title\\": \\"Title\\", - \\"this0_seasons0_node_number\\": { - \\"low\\": 1, - \\"high\\": 0 - }, - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [], - \\"sub\\": \\"michel\\" - }, - \\"authorization_0_1_0_0_after_param2\\": \\"verified\\", - \\"authorization_0_1_0_0_after_param3\\": \\"creator\\", - \\"this0_publisher_connect0_node_param0\\": \\"ID\\", - \\"authorization_0_after_param2\\": \\"verified\\", - \\"authorization_0_after_param3\\": \\"creator\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/3905.test.ts b/packages/graphql/tests/tck/issues/3905.test.ts index 9f6f52fb8c..28b410c837 100644 --- a/packages/graphql/tests/tck/issues/3905.test.ts +++ b/packages/graphql/tests/tck/issues/3905.test.ts @@ -20,7 +20,9 @@ import { Neo4jGraphQL } from "../../../src"; import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; -describe("https://github.com/neo4j/graphql/issues/3905", () => { +// This has to be reintroduced when user defined types are supported as target for cypher fields +// eslint-disable-next-line jest/no-disabled-tests +describe.skip("https://github.com/neo4j/graphql/issues/3905", () => { let typeDefs: string; let neoSchema: Neo4jGraphQL; @@ -37,19 +39,11 @@ describe("https://github.com/neo4j/graphql/issues/3905", () => { ) } - type pathList - @query(read: false, aggregate: false) - @mutation(operations: []) - @subscription(events: []) - @node { - paths: [[pathLink]] + type pathList @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { + paths: [[pathLink!]] } - type pathLink - @query(read: false, aggregate: false) - @mutation(operations: []) - @subscription(events: []) - @node { + type pathLink @query(read: false, aggregate: false) @mutation(operations: []) @subscription(events: []) { entity_id: Int other_entity_id: Int } diff --git a/packages/graphql/tests/tck/issues/4001.test.ts b/packages/graphql/tests/tck/issues/4001.test.ts index 3553a1ade9..0334f5c241 100644 --- a/packages/graphql/tests/tck/issues/4001.test.ts +++ b/packages/graphql/tests/tck/issues/4001.test.ts @@ -68,7 +68,8 @@ describe("https://github.com/neo4j/graphql/issues/4001", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Serie) + "CYPHER 5 + MATCH (this:Serie) CALL { WITH this CALL { @@ -121,7 +122,8 @@ describe("https://github.com/neo4j/graphql/issues/4001", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Serie) + "CYPHER 5 + MATCH (this:Serie) CALL { WITH this CALL { @@ -167,7 +169,8 @@ describe("https://github.com/neo4j/graphql/issues/4001", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Serie) + "CYPHER 5 + MATCH (this:Serie) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/4004.test.ts b/packages/graphql/tests/tck/issues/4004.test.ts index 9e9dc368b5..5b89c56b8b 100644 --- a/packages/graphql/tests/tck/issues/4004.test.ts +++ b/packages/graphql/tests/tck/issues/4004.test.ts @@ -62,7 +62,8 @@ describe("https://github.com/neo4j/graphql/issues/4004", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Series) + "CYPHER 5 + MATCH (this:Series) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/4007.test.ts b/packages/graphql/tests/tck/issues/4007.test.ts index 783d150139..c1152bda12 100644 --- a/packages/graphql/tests/tck/issues/4007.test.ts +++ b/packages/graphql/tests/tck/issues/4007.test.ts @@ -60,7 +60,8 @@ describe("https://github.com/neo4j/graphql/issues/4007", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/issues/4015.test.ts b/packages/graphql/tests/tck/issues/4015.test.ts index c9c1b13fb3..f730bed1f4 100644 --- a/packages/graphql/tests/tck/issues/4015.test.ts +++ b/packages/graphql/tests/tck/issues/4015.test.ts @@ -64,7 +64,8 @@ describe("https://github.com/neo4j/graphql/issues/4015", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) diff --git a/packages/graphql/tests/tck/issues/402.test.ts b/packages/graphql/tests/tck/issues/402.test.ts deleted file mode 100644 index d8ebf8c465..0000000000 --- a/packages/graphql/tests/tck/issues/402.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("#402", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Event @node { - id: ID! - area: Area! @relationship(type: "HAPPENS_IN", direction: OUT) - } - - type Area @node { - id: ID! - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("Should ignore the empty array and not include any where", async () => { - const query = /* GraphQL */ ` - query ($area: [ID!]) { - events(where: { area: { id_IN: $area } }) { - id - area { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Event) - CALL { - WITH this - MATCH (this)-[this0:HAPPENS_IN]->(this1:Area) - WITH this1 { .id } AS this1 - RETURN head(collect(this1)) AS var2 - } - RETURN this { .id, area: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4077.test.ts b/packages/graphql/tests/tck/issues/4077.test.ts deleted file mode 100644 index 06b8efbd1c..0000000000 --- a/packages/graphql/tests/tck/issues/4077.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4077", () => { - const secret = "sssh!"; - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type PreviewClip @mutation(operations: [DELETE]) @node { - id: ID! @id - startTime: Int! - duration: Int! - - markedAsDone: Boolean! @default(value: false) - - clippedFrom: Video! @relationship(type: "VIDEO_HAS_PREVIEW_CLIP", direction: IN) - - creationDate: DateTime! @timestamp(operations: [CREATE]) - lastUpdate: DateTime! @timestamp(operations: [CREATE, UPDATE]) - } - - extend type PreviewClip - @authorization( - filter: [ - { where: { node: { clippedFrom: { publisher: { id_EQ: "$jwt.sub" } } } } } - { where: { jwt: { roles_INCLUDES: "admin" } } } - ] - ) - - type Video @mutation(operations: [UPDATE]) @node { - id: ID! @id - - publisher: User! @relationship(type: "PUBLISHER", direction: IN) - - creationDate: DateTime! @timestamp(operations: [CREATE]) - lastUpdate: DateTime! @timestamp(operations: [CREATE, UPDATE]) - processing: String! - clips: [PreviewClip!]! @relationship(type: "VIDEO_HAS_PREVIEW_CLIP", direction: OUT) - } - - extend type Video - @authorization( - filter: [ - { where: { node: { publisher: { id_EQ: "$jwt.sub" } } } } - { where: { jwt: { roles_INCLUDES: "admin" } } } - { - requireAuthentication: false - operations: [READ] - where: { node: { processing_EQ: "published" } } - } - ] - ) - - type User @mutation(operations: [UPDATE]) @node { - id: ID! @id - } - - extend type User - @authorization( - validate: [ - { operations: [UPDATE], where: { node: { id_EQ: "$jwt.sub" } } } - { operations: [UPDATE], where: { jwt: { roles_INCLUDES: "admin" } } } - ] - ) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("wrap authenticated subquery on top level read operation", async () => { - const query = /* GraphQL */ ` - query listPossiblePreviewClips { - previewClips(where: { clippedFrom: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { - id - } - } - `; - const token = createBearerToken(secret, { sub: "michel", roles: ["admin"] }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PreviewClip) - OPTIONAL MATCH (this)<-[:VIDEO_HAS_PREVIEW_CLIP]-(this0:Video) - WITH *, count(this0) AS var1 - CALL { - WITH this - MATCH (this)<-[:VIDEO_HAS_PREVIEW_CLIP]-(this2:Video) - OPTIONAL MATCH (this2)<-[:PUBLISHER]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE (var4 <> 0 AND ($jwt.sub IS NOT NULL AND this3.id = $jwt.sub)) - RETURN count(this2) = 1 AS var5 - } - WITH * - WHERE ((NOT (this.markedAsDone = $param1) AND (var1 <> 0 AND this0.id = $param2)) AND (($isAuthenticated = true AND var5 = true) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)))) - RETURN this { .id } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"jwt\\": { - \\"roles\\": [ - \\"admin\\" - ], - \\"sub\\": \\"michel\\" - }, - \\"param1\\": true, - \\"param2\\": \\"1234\\", - \\"isAuthenticated\\": true, - \\"param4\\": \\"admin\\" - }" - `); - }); - - test("wrap authenticated subquery on nested read operation", async () => { - const query = /* GraphQL */ ` - query { - videos { - clips(where: { clippedFrom: { id_EQ: "1234" }, NOT: { markedAsDone_EQ: true } }) { - id - } - } - } - `; - const token = createBearerToken(secret, { sub: "michel", roles: ["admin"] }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Video) - OPTIONAL MATCH (this)<-[:PUBLISHER]-(this0:User) - WITH *, count(this0) AS var1 - WITH * - WHERE (($isAuthenticated = true AND (var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub))) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)) OR ($param3 IS NOT NULL AND this.processing = $param3)) - CALL { - WITH this - MATCH (this)-[this2:VIDEO_HAS_PREVIEW_CLIP]->(this3:PreviewClip) - OPTIONAL MATCH (this3)<-[:VIDEO_HAS_PREVIEW_CLIP]-(this4:Video) - WITH *, count(this4) AS var5 - CALL { - WITH this3 - MATCH (this3)<-[:VIDEO_HAS_PREVIEW_CLIP]-(this6:Video) - OPTIONAL MATCH (this6)<-[:PUBLISHER]-(this7:User) - WITH *, count(this7) AS var8 - WITH * - WHERE (var8 <> 0 AND ($jwt.sub IS NOT NULL AND this7.id = $jwt.sub)) - RETURN count(this6) = 1 AS var9 - } - WITH * - WHERE ((NOT (this3.markedAsDone = $param4) AND (var5 <> 0 AND this4.id = $param5)) AND (($isAuthenticated = true AND var9 = true) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param6 IN $jwt.roles)))) - WITH this3 { .id } AS this3 - RETURN collect(this3) AS var10 - } - RETURN this { clips: var10 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [ - \\"admin\\" - ], - \\"sub\\": \\"michel\\" - }, - \\"param2\\": \\"admin\\", - \\"param3\\": \\"published\\", - \\"param4\\": true, - \\"param5\\": \\"1234\\", - \\"param6\\": \\"admin\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4095.test.ts b/packages/graphql/tests/tck/issues/4095.test.ts index c66c43a253..90eb23c970 100644 --- a/packages/graphql/tests/tck/issues/4095.test.ts +++ b/packages/graphql/tests/tck/issues/4095.test.ts @@ -29,19 +29,21 @@ describe("https://github.com/neo4j/graphql/issues/4095", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: User! @relationship(type: "CREATOR_OF", direction: IN) + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) } - type Person @authorization(filter: [{ where: { node: { creator: { id_EQ: "$jwt.uid" } } } }]) @node { - id: ID! @id @unique - creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) - family: Family! @relationship(type: "MEMBER_OF", direction: OUT) + type Person + @authorization(filter: [{ where: { node: { creator: { some: { id: { eq: "$jwt.uid" } } } } } }]) + @node { + id: ID! @id + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + family: [Family!]! @relationship(type: "MEMBER_OF", direction: OUT) } extend schema @authentication `; @@ -71,17 +73,18 @@ describe("https://github.com/neo4j/graphql/issues/4095", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Family) + "CYPHER 5 + MATCH (this:Family) CALL { WITH this MATCH (this)<-[this0:MEMBER_OF]-(this1:Person) - OPTIONAL MATCH (this1)<-[:CREATOR_OF]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE ($isAuthenticated = true AND (var3 <> 0 AND ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid))) - RETURN count(this1) AS var4 + WHERE ($isAuthenticated = true AND EXISTS { + MATCH (this1)<-[:CREATOR_OF]-(this2:User) + WHERE ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid) + }) + RETURN count(this1) AS var3 } - RETURN this { .id, membersAggregate: { count: var4 } } AS this" + RETURN this { .id, membersAggregate: { count: var3 } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/issues/4110.test.ts b/packages/graphql/tests/tck/issues/4110.test.ts deleted file mode 100644 index 6e1fec7712..0000000000 --- a/packages/graphql/tests/tck/issues/4110.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4110", () => { - const secret = "sssh!"; - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Company - @node - @authorization( - filter: [{ operations: [READ], where: { node: { inBetween: { company: { id_EQ: "example" } } } } }] - ) { - id: ID @id - inBetween: InBetween @relationship(type: "CONNECT_TO", direction: OUT) - } - type InBetween @node { - id: ID @id - company: Company! @relationship(type: "CONNECT_TO", direction: IN) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("wrap authenticated subquery on top level read operation", async () => { - const query = /* GraphQL */ ` - query { - companies { - inBetween { - company { - id - } - } - } - } - `; - const token = createBearerToken(secret, { sub: "michel", roles: ["admin"] }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Company) - CALL { - WITH this - MATCH (this)-[:CONNECT_TO]->(this0:InBetween) - OPTIONAL MATCH (this0)<-[:CONNECT_TO]-(this1:Company) - WITH *, count(this1) AS var2 - WITH * - WHERE (var2 <> 0 AND ($param0 IS NOT NULL AND this1.id = $param0)) - RETURN count(this0) = 1 AS var3 - } - WITH * - WHERE ($isAuthenticated = true AND var3 = true) - CALL { - WITH this - MATCH (this)-[this4:CONNECT_TO]->(this5:InBetween) - CALL { - WITH this5 - MATCH (this5)<-[this6:CONNECT_TO]-(this7:Company) - CALL { - WITH this7 - MATCH (this7)-[:CONNECT_TO]->(this8:InBetween) - OPTIONAL MATCH (this8)<-[:CONNECT_TO]-(this9:Company) - WITH *, count(this9) AS var10 - WITH * - WHERE (var10 <> 0 AND ($param2 IS NOT NULL AND this9.id = $param2)) - RETURN count(this8) = 1 AS var11 - } - WITH * - WHERE ($isAuthenticated = true AND var11 = true) - WITH this7 { .id } AS this7 - RETURN head(collect(this7)) AS var12 - } - WITH this5 { company: var12 } AS this5 - RETURN head(collect(this5)) AS var13 - } - RETURN this { inBetween: var13 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"example\\", - \\"isAuthenticated\\": true, - \\"param2\\": \\"example\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4115.test.ts b/packages/graphql/tests/tck/issues/4115.test.ts index 6d754af6d8..472bec48a0 100644 --- a/packages/graphql/tests/tck/issues/4115.test.ts +++ b/packages/graphql/tests/tck/issues/4115.test.ts @@ -29,14 +29,14 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! roles: [String!]! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: User! @relationship(type: "CREATOR_OF", direction: IN) + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) } type Person @@ -46,16 +46,22 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { { where: { AND: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { family: { creator: { roles_INCLUDES: "plan:paid" } } } } + { node: { creator: { some: { id: { eq: "$jwt.uid" } } } } } + { + node: { + family: { + some: { creator: { some: { roles: { includes: "plan:paid" } } } } + } + } + } ] } } ] ) { - id: ID! @id @unique - creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) - family: Family! @relationship(type: "MEMBER_OF", direction: OUT) + id: ID! @id + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + family: [Family!]! @relationship(type: "MEMBER_OF", direction: OUT) } type JWT @jwt { @@ -90,38 +96,36 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Family) + "CYPHER 5 + MATCH (this:Family) CALL { WITH this MATCH (this)<-[this0:MEMBER_OF]-(this1:Person) - OPTIONAL MATCH (this1)<-[:CREATOR_OF]-(this2:User) - WITH *, count(this2) AS var3 - CALL { - WITH this1 - MATCH (this1)-[:MEMBER_OF]->(this4:Family) - OPTIONAL MATCH (this4)<-[:CREATOR_OF]-(this5:User) - WITH *, count(this5) AS var6 - WITH * - WHERE (var6 <> 0 AND ($param0 IS NOT NULL AND $param0 IN this5.roles)) - RETURN count(this4) = 1 AS var7 - } - WITH * - WHERE ($isAuthenticated = true AND ((var3 <> 0 AND ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid)) AND var7 = true)) - RETURN count(this1) AS var8 + WHERE ($isAuthenticated = true AND (EXISTS { + MATCH (this1)<-[:CREATOR_OF]-(this2:User) + WHERE ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid) + } AND EXISTS { + MATCH (this1)-[:MEMBER_OF]->(this3:Family) + WHERE EXISTS { + MATCH (this3)<-[:CREATOR_OF]-(this4:User) + WHERE ($param2 IS NOT NULL AND $param2 IN this4.roles) + } + })) + RETURN count(this1) AS var5 } - RETURN this { .id, membersAggregate: { count: var8 } } AS this" + RETURN this { .id, membersAggregate: { count: var5 } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": \\"plan:paid\\", \\"isAuthenticated\\": true, \\"jwt\\": { \\"roles\\": [ \\"admin\\" ], \\"sub\\": \\"michel\\" - } + }, + \\"param2\\": \\"plan:paid\\" }" `); }); diff --git a/packages/graphql/tests/tck/issues/4116.test.ts b/packages/graphql/tests/tck/issues/4116.test.ts index 6f141912f0..8a8e41564d 100644 --- a/packages/graphql/tests/tck/issues/4116.test.ts +++ b/packages/graphql/tests/tck/issues/4116.test.ts @@ -21,7 +21,7 @@ import { Neo4jGraphQL } from "../../../src"; import { createBearerToken } from "../../utils/create-bearer-token"; import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; -describe("https://github.com/neo4j/graphql/issues/4115", () => { +describe("https://github.com/neo4j/graphql/issues/4116", () => { const secret = "sssh!"; let typeDefs: string; let neoSchema: Neo4jGraphQL; @@ -29,24 +29,30 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique + id: ID! roles: [String!]! } type Family @node { - id: ID! @id @unique + id: ID! @id members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: User! @relationship(type: "CREATOR_OF", direction: IN) + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) } type Person @node @authorization( - filter: [{ where: { node: { family: { creator: { roles_INCLUDES: "plan:paid" } } } } }] + filter: [ + { + where: { + node: { family: { some: { creator: { some: { roles: { includes: "plan:paid" } } } } } } + } + } + ] ) { - id: ID! @id @unique - creator: User! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) - family: Family! @relationship(type: "MEMBER_OF", direction: OUT) + id: ID! @id + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) + family: [Family!]! @relationship(type: "MEMBER_OF", direction: OUT) } type JWT @jwt { @@ -81,30 +87,27 @@ describe("https://github.com/neo4j/graphql/issues/4115", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Family) + "CYPHER 5 + MATCH (this:Family) CALL { WITH this MATCH (this)<-[this0:MEMBER_OF]-(this1:Person) - CALL { - WITH this1 + WHERE ($isAuthenticated = true AND EXISTS { MATCH (this1)-[:MEMBER_OF]->(this2:Family) - OPTIONAL MATCH (this2)<-[:CREATOR_OF]-(this3:User) - WITH *, count(this3) AS var4 - WITH * - WHERE (var4 <> 0 AND ($param0 IS NOT NULL AND $param0 IN this3.roles)) - RETURN count(this2) = 1 AS var5 - } - WITH * - WHERE ($isAuthenticated = true AND var5 = true) - RETURN count(this1) AS var6 + WHERE EXISTS { + MATCH (this2)<-[:CREATOR_OF]-(this3:User) + WHERE ($param1 IS NOT NULL AND $param1 IN this3.roles) + } + }) + RETURN count(this1) AS var4 } - RETURN this { .id, membersAggregate: { count: var6 } } AS this" + RETURN this { .id, membersAggregate: { count: var4 } } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": \\"plan:paid\\", - \\"isAuthenticated\\": true + \\"isAuthenticated\\": true, + \\"param1\\": \\"plan:paid\\" }" `); }); diff --git a/packages/graphql/tests/tck/issues/4118.test.ts b/packages/graphql/tests/tck/issues/4118.test.ts deleted file mode 100644 index fed65530d6..0000000000 --- a/packages/graphql/tests/tck/issues/4118.test.ts +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/2871", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = /* GraphQL */ ` - type JWT @jwt { - id: String - roles: [String] - } - type User - @node - @authorization( - validate: [ - { where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] } - { where: { jwt: { roles_INCLUDES: "overlord" } } } - ] - ) { - userId: String! @unique - adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) - } - - type Tenant - @node - @authorization( - validate: [ - { where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } } - { where: { jwt: { roles_INCLUDES: "overlord" } } } - ] - ) { - id: ID! @id - settings: Settings! @relationship(type: "HAS_SETTINGS", direction: OUT) - admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN) - } - - type Settings - @node - @authorization( - validate: [ - { where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } - { where: { jwt: { roles_INCLUDES: "overlord" } } } - ] - ) { - id: ID! @id - tenant: Tenant! @relationship(type: "HAS_SETTINGS", direction: IN) - openingDays: [OpeningDay!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) - name: String - } - - type OpeningDay - @node - @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] - ) { - settings: Settings @relationship(type: "VALID_OPENING_DAYS", direction: IN) - id: ID! @id - name: String - } - - type LOL - @authorization(validate: [{ where: { node: { host: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) - @node { - host: Tenant! @relationship(type: "HOSTED_BY", direction: OUT) - openingDays: [OpeningDay!]! @relationship(type: "HAS_OPENING_DAY", direction: OUT) - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should include checks for auth jwt param is not null", async () => { - const query = /* GraphQL */ ` - mutation addLols($input: [LOLCreateInput!]!) { - createLols(input: $input) { - lols { - host { - id - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - variableValues: { - input: { - host: { - connect: { - where: { - node: { - id_EQ: "userid", - }, - }, - }, - }, - openingDays: { - connect: { - where: { - node: { - id_EQ: "openingdayid", - }, - }, - }, - }, - }, - }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:LOL) - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_host_connect0_node:Tenant) - WHERE this0_host_connect0_node.id = $this0_host_connect0_node_param0 AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND size([(this0_host_connect0_node)<-[:ADMIN_IN]-(authorization_0_before_this0:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_before_this0.userId = $jwt.id) | 1]) > 0) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH * - WITH collect(this0_host_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_host_connect0_node - MERGE (this0)-[:HOSTED_BY]->(this0_host_connect0_node) - } - } - WITH this0, this0_host_connect0_node - WITH * - OPTIONAL MATCH (this0)-[:HOSTED_BY]->(authorization_0_after_this2:Tenant) - WITH *, count(authorization_0_after_this2) AS authorization_0_after_var0 - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_after_var0 <> 0 AND size([(authorization_0_after_this2)<-[:ADMIN_IN]-(authorization_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT (($isAuthenticated = true AND size([(this0_host_connect0_node)<-[:ADMIN_IN]-(authorization_0_after_this3:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this3.userId = $jwt.id) | 1]) > 0) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - RETURN count(*) AS connect_this0_host_connect_Tenant0 - } - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_openingDays_connect0_node:OpeningDay) - CALL { - WITH this0_openingDays_connect0_node - MATCH (this0_openingDays_connect0_node)<-[:VALID_OPENING_DAYS]-(authorization_0_before_this1:Settings) - OPTIONAL MATCH (authorization_0_before_this1)<-[:HAS_SETTINGS]-(authorization_0_before_this2:Tenant) - WITH *, count(authorization_0_before_this2) AS authorization_0_before_var3 - WITH * - WHERE (authorization_0_before_var3 <> 0 AND size([(authorization_0_before_this2)<-[:ADMIN_IN]-(authorization_0_before_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_before_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_before_this1) = 1 AS authorization_0_before_var0 - } - WITH * - WHERE this0_openingDays_connect0_node.id = $this0_openingDays_connect0_node_param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_before_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH * - WITH collect(this0_openingDays_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_openingDays_connect0_node - MERGE (this0)-[:HAS_OPENING_DAY]->(this0_openingDays_connect0_node) - } - } - WITH this0, this0_openingDays_connect0_node - WITH * - OPTIONAL MATCH (this0)-[:HOSTED_BY]->(authorization_0_after_this2:Tenant) - WITH *, count(authorization_0_after_this2) AS authorization_0_after_var0 - CALL { - WITH this0_openingDays_connect0_node - MATCH (this0_openingDays_connect0_node)<-[:VALID_OPENING_DAYS]-(authorization_0_after_this4:Settings) - OPTIONAL MATCH (authorization_0_after_this4)<-[:HAS_SETTINGS]-(authorization_0_after_this5:Tenant) - WITH *, count(authorization_0_after_this5) AS authorization_0_after_var6 - WITH * - WHERE (authorization_0_after_var6 <> 0 AND size([(authorization_0_after_this5)<-[:ADMIN_IN]-(authorization_0_after_this7:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this7.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_after_this4) = 1 AS authorization_0_after_var3 - } - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_after_var0 <> 0 AND size([(authorization_0_after_this2)<-[:ADMIN_IN]-(authorization_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_after_var3 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0])) - RETURN count(*) AS connect_this0_openingDays_connect_OpeningDay0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_host_Tenant_unique:HOSTED_BY]->(:Tenant) - WITH count(this0_host_Tenant_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDLOL.host required exactly once', [0]) - RETURN c AS this0_host_Tenant_unique_ignored - } - WITH * - OPTIONAL MATCH (this0)-[:HOSTED_BY]->(authorization_0_after_this2:Tenant) - WITH *, count(authorization_0_after_this2) AS authorization_0_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_after_var0 <> 0 AND size([(authorization_0_after_this2)<-[:ADMIN_IN]-(authorization_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HOSTED_BY]->(create_this1:Tenant) - WHERE apoc.util.validatePredicate(NOT (($isAuthenticated = true AND size([(create_this1)<-[:ADMIN_IN]-(create_this2:User) WHERE ($jwt.id IS NOT NULL AND create_this2.userId = $jwt.id) | 1]) > 0) OR ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param2 IN $jwt.roles))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this1 { .id } AS create_this1 - RETURN head(collect(create_this1)) AS create_var3 - } - RETURN this0 { host: create_var3 } AS create_var4 - } - RETURN [create_var4] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": false, - \\"jwt\\": {}, - \\"create_param2\\": \\"overlord\\", - \\"this0_host_connect0_node_param0\\": \\"userid\\", - \\"authorization_0_before_param2\\": \\"overlord\\", - \\"authorization_0_after_param2\\": \\"overlord\\", - \\"this0_openingDays_connect0_node_param0\\": \\"openingdayid\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4170.test.ts b/packages/graphql/tests/tck/issues/4170.test.ts index b9cfec05a1..1ec5d7743b 100644 --- a/packages/graphql/tests/tck/issues/4170.test.ts +++ b/packages/graphql/tests/tck/issues/4170.test.ts @@ -28,22 +28,30 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { id: String roles: [String] } - type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + type User + @authorization(validate: [{ where: { node: { userId: { eq: "$jwt.id" } } }, operations: [READ] }]) + @node { + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } - type Tenant @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { + type Tenant + @authorization(validate: [{ where: { node: { admins: { some: { userId: { eq: "$jwt.id" } } } } } }]) + @node { id: ID! @id - settings: Settings! @relationship(type: "HAS_SETTINGS", direction: OUT) + settings: [Settings!]! @relationship(type: "HAS_SETTINGS", direction: OUT) admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN) } type Settings - @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) + @authorization( + validate: [ + { where: { node: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } } } + ] + ) @node { id: ID! @id - tenant: Tenant! @relationship(type: "HAS_SETTINGS", direction: IN) + tenant: [Tenant!]! @relationship(type: "HAS_SETTINGS", direction: IN) openingDays: [OpeningDay!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) name: String } @@ -51,10 +59,20 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { type OpeningDay @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [ + { + where: { + node: { + settings: { + some: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } + } + } + } + } + ] ) { id: ID! @id - settings: Settings @relationship(type: "VALID_GARAGES", direction: IN) + settings: [Settings!]! @relationship(type: "VALID_GARAGES", direction: IN) open: [OpeningHoursInterval!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) } @@ -64,13 +82,23 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { validate: [ { where: { - node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } + node: { + openingDay: { + some: { + settings: { + some: { + tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } + } + } + } + } + } } } ] ) { name: String - openingDay: OpeningDay! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) + openingDay: [OpeningDay!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } `; @@ -134,7 +162,8 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Tenant) SET this0.id = randomUUID() WITH * @@ -148,74 +177,44 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { SET this0_settings0_node_openingDays0_node_open0_node.updatedBy = $resolvedCallbacks.this0_settings0_node_openingDays0_node_open0_node_updatedBy_getUserIDFromContext SET this0_settings0_node_openingDays0_node_open0_node.name = $this0_settings0_node_openingDays0_node_open0_node_name MERGE (this0_settings0_node_openingDays0_node)-[:HAS_OPEN_INTERVALS]->(this0_settings0_node_openingDays0_node_open0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique:HAS_OPEN_INTERVALS]-(:OpeningDay) - WITH count(this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningHoursInterval.openingDay required exactly once', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique_ignored - } MERGE (this0_settings0_node)-[:VALID_OPENING_DAYS]->(this0_settings0_node_openingDays0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[this0_settings0_node_openingDays0_node_settings_Settings_unique:VALID_GARAGES]-(:Settings) - WITH count(this0_settings0_node_openingDays0_node_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningDay.settings must be less than or equal to one', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_settings_Settings_unique_ignored - } MERGE (this0)-[:HAS_SETTINGS]->(this0_settings0_node) WITH * - CALL { - WITH this0_settings0_node - MATCH (this0_settings0_node)<-[this0_settings0_node_tenant_Tenant_unique:HAS_SETTINGS]-(:Tenant) - WITH count(this0_settings0_node_tenant_Tenant_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSettings.tenant required exactly once', [0]) - RETURN c AS this0_settings0_node_tenant_Tenant_unique_ignored - } - WITH * CREATE (this0_admins0_node:User) SET this0_admins0_node.userId = $this0_admins0_node_userId MERGE (this0)<-[:ADMIN_IN]-(this0_admins0_node) WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_settings_Settings_unique:HAS_SETTINGS]->(:Settings) - WITH count(this0_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDTenant.settings required exactly once', [0]) - RETURN c AS this0_settings_Settings_unique_ignored - } - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this1:OpeningDay) - CALL { - WITH authorization_0_0_0_0_0_0_0_0_0_0_after_this1 - MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this1)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this2:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this2)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this3:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_0_0_0_after_this3) AS authorization_0_0_0_0_0_0_0_0_0_0_after_var4 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_0_0_0_after_var4 <> 0 AND size([(authorization_0_0_0_0_0_0_0_0_0_0_after_this3)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this5:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_0_0_0_after_this5.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this2) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var6 + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this0:OpeningDay) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this0)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this1:Settings) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this1)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this2:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this3:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_0_0_0_after_this3.userId = $jwt.id) + } + } } - WITH * - WHERE authorization_0_0_0_0_0_0_0_0_0_0_after_var6 = true - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_after_this1:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_after_this1)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_after_this2) AS authorization_0_0_0_0_0_0_0_after_var3 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_after_var3 <> 0 AND size([(authorization_0_0_0_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_after_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_after_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_after_var0 - } - OPTIONAL MATCH (this0_settings0_node)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_after_this2) AS authorization_0_0_0_0_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_0_0_0_after_var0 <> 0 AND size([(authorization_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:ADMIN_IN]-(authorization_0_after_this0:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node_openingDays0_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_after_this0:Settings) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_after_this0)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_after_this1:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_after_this1)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_after_this2:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_after_this2.userId = $jwt.id) + } + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_after_this0:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_after_this0)<-[:ADMIN_IN]-(authorization_0_0_0_0_after_this1:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_after_this1.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[:ADMIN_IN]-(authorization_0_after_this0:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this0.userId = $jwt.id) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this0 } CALL { @@ -224,6 +223,7 @@ describe("https://github.com/neo4j/graphql/issues/4170", () => { WITH this0 MATCH (this0)<-[create_this0:ADMIN_IN]-(create_this1:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.id IS NOT NULL AND create_this1.userId = $jwt.id)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this1 WITH create_this1 { .userId } AS create_this1 RETURN collect(create_this1) AS create_var2 } diff --git a/packages/graphql/tests/tck/issues/4214.test.ts b/packages/graphql/tests/tck/issues/4214.test.ts index f03b498bd2..68de3a006e 100644 --- a/packages/graphql/tests/tck/issues/4214.test.ts +++ b/packages/graphql/tests/tck/issues/4214.test.ts @@ -34,29 +34,29 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { } type User @node { - id: ID! @id @unique + id: ID! @id email: String! roles: [String!]! - store: Store @relationship(type: "WORKS_AT", direction: OUT) + store: [Store!]! @relationship(type: "WORKS_AT", direction: OUT) } type Store @node { - id: ID! @id @unique + id: ID! @id name: String! employees: [User!]! @relationship(type: "WORKS_AT", direction: IN) transactions: [Transaction!]! @relationship(type: "TRANSACTION", direction: IN) } type Transaction @node { - id: ID! @id @unique - store: Store! @relationship(type: "TRANSACTION", direction: OUT) + id: ID! @id + store: [Store!]! @relationship(type: "TRANSACTION", direction: OUT) type: String! items: [TransactionItem!]! @relationship(type: "ITEM_TRANSACTED", direction: IN) completed: Boolean } type TransactionItem @node { - transaction: Transaction @relationship(type: "ITEM_TRANSACTED", direction: OUT) + transaction: [Transaction!]! @relationship(type: "ITEM_TRANSACTED", direction: OUT) name: String price: Float quantity: Int @@ -70,8 +70,11 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { operations: [CREATE, CREATE_RELATIONSHIP] where: { - OR: [{ jwt: { roles_INCLUDES: "store-owner" } }, { jwt: { roles_INCLUDES: "employee" } }] - node: { store: { id_EQ: "$jwt.store" } } + OR: [ + { jwt: { roles: { includes: "store-owner" } } } + { jwt: { roles: { includes: "employee" } } } + ] + node: { store: { some: { id: { eq: "$jwt.store" } } } } } } ] @@ -79,11 +82,14 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { extend type Transaction @authorization( filter: [ - { where: { jwt: { roles_INCLUDES: "admin" } } } + { where: { jwt: { roles: { includes: "admin" } } } } { where: { - OR: [{ jwt: { roles_INCLUDES: "store-owner" } }, { jwt: { roles_INCLUDES: "employee" } }] - node: { store: { id_EQ: "$jwt.store" } } + OR: [ + { jwt: { roles: { includes: "store-owner" } } } + { jwt: { roles: { includes: "employee" } } } + ] + node: { store: { some: { id: { eq: "$jwt.store" } } } } } } ] @@ -97,8 +103,11 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { { operations: [CREATE, CREATE_RELATIONSHIP] where: { - OR: [{ jwt: { roles_INCLUDES: "store-owner" } }, { jwt: { roles_INCLUDES: "employee" } }] - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + OR: [ + { jwt: { roles: { includes: "store-owner" } } } + { jwt: { roles: { includes: "employee" } } } + ] + node: { transaction: { some: { store: { some: { id: { eq: "$jwt.store" } } } } } } } } ] @@ -106,11 +115,14 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { extend type TransactionItem @authorization( filter: [ - { where: { jwt: { roles_INCLUDES: "admin" } } } + { where: { jwt: { roles: { includes: "admin" } } } } { where: { - OR: [{ jwt: { roles_INCLUDES: "store-owner" } }, { jwt: { roles_INCLUDES: "employee" } }] - node: { transaction: { store: { id_EQ: "$jwt.store" } } } + OR: [ + { jwt: { roles: { includes: "store-owner" } } } + { jwt: { roles: { includes: "employee" } } } + ] + node: { transaction: { some: { store: { some: { id: { eq: "$jwt.store" } } } } } } } } ] @@ -136,7 +148,7 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { name: "Milk" price: 5 quantity: 1 - transaction: { connect: { where: { node: { id_EQ: "transactionid" } } } } + transaction: { connect: { where: { node: { id: { eq: "transactionid" } } } } } } ) { transactionItems { @@ -158,7 +170,8 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:TransactionItem) SET this0.name = $this0_name SET this0.price = $this0_price @@ -167,12 +180,13 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { CALL { WITH this0 OPTIONAL MATCH (this0_transaction_connect0_node:Transaction) - OPTIONAL MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_before_this1:Store) - WITH *, count(authorization_0_before_this1) AS authorization_0_before_var0 - OPTIONAL MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_before_this3:Store) - WITH *, count(authorization_0_before_this3) AS authorization_0_before_var2 - WITH * - WHERE this0_transaction_connect0_node.id = $this0_transaction_connect0_node_param0 AND ((($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_before_param4 IN $jwt.roles)) AND (authorization_0_before_var0 <> 0 AND ($jwt.store IS NOT NULL AND authorization_0_before_this1.id = $jwt.store)))) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_before_param5 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_before_param6 IN $jwt.roles)) AND (authorization_0_before_var2 <> 0 AND ($jwt.store IS NOT NULL AND authorization_0_before_this3.id = $jwt.store))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WHERE this0_transaction_connect0_node.id = $this0_transaction_connect0_node_param0 AND ((($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_before_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_before_param3 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_before_param4 IN $jwt.roles)) AND EXISTS { + MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_before_this0:Store) + WHERE ($jwt.store IS NOT NULL AND authorization_0_before_this0.id = $jwt.store) + })) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_before_param5 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_before_param6 IN $jwt.roles)) AND EXISTS { + MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_before_this1:Store) + WHERE ($jwt.store IS NOT NULL AND authorization_0_before_this1.id = $jwt.store) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) CALL { WITH * WITH collect(this0_transaction_connect0_node) as connectedNodes, collect(this0) as parentNodes @@ -180,70 +194,56 @@ describe("https://github.com/neo4j/graphql/issues/4214", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_transaction_connect0_node - MERGE (this0)-[:ITEM_TRANSACTED]->(this0_transaction_connect0_node) + CREATE (this0)-[:ITEM_TRANSACTED]->(this0_transaction_connect0_node) } } WITH this0, this0_transaction_connect0_node - WITH * - CALL { - WITH this0 - MATCH (this0)-[:ITEM_TRANSACTED]->(authorization_0_after_this3:Transaction) - OPTIONAL MATCH (authorization_0_after_this3)-[:TRANSACTION]->(authorization_0_after_this4:Store) - WITH *, count(authorization_0_after_this4) AS authorization_0_after_var5 - WITH * - WHERE (authorization_0_after_var5 <> 0 AND ($jwt.store IS NOT NULL AND authorization_0_after_this4.id = $jwt.store)) - RETURN count(authorization_0_after_this3) = 1 AS authorization_0_after_var0 - } - OPTIONAL MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_after_this2:Store) - WITH *, count(authorization_0_after_this2) AS authorization_0_after_var1 - WITH * - WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles)) AND authorization_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles)) AND (authorization_0_after_var1 <> 0 AND ($jwt.store IS NOT NULL AND authorization_0_after_this2.id = $jwt.store))), \\"@neo4j/graphql/FORBIDDEN\\", [0])) + WITH this0, this0_transaction_connect0_node + WHERE (apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles)) AND EXISTS { + MATCH (this0)-[:ITEM_TRANSACTED]->(authorization_0_after_this0:Transaction) + WHERE EXISTS { + MATCH (authorization_0_after_this0)-[:TRANSACTION]->(authorization_0_after_this1:Store) + WHERE ($jwt.store IS NOT NULL AND authorization_0_after_this1.id = $jwt.store) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param4 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param5 IN $jwt.roles)) AND EXISTS { + MATCH (this0_transaction_connect0_node)-[:TRANSACTION]->(authorization_0_after_this2:Store) + WHERE ($jwt.store IS NOT NULL AND authorization_0_after_this2.id = $jwt.store) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0])) RETURN count(*) AS connect_this0_transaction_connect_Transaction0 } WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_transaction_Transaction_unique:ITEM_TRANSACTED]->(:Transaction) - WITH count(this0_transaction_Transaction_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDTransactionItem.transaction must be less than or equal to one', [0]) - RETURN c AS this0_transaction_Transaction_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[:ITEM_TRANSACTED]->(authorization_0_after_this1:Transaction) - OPTIONAL MATCH (authorization_0_after_this1)-[:TRANSACTION]->(authorization_0_after_this2:Store) - WITH *, count(authorization_0_after_this2) AS authorization_0_after_var3 - WITH * - WHERE (authorization_0_after_var3 <> 0 AND ($jwt.store IS NOT NULL AND authorization_0_after_this2.id = $jwt.store)) - RETURN count(authorization_0_after_this1) = 1 AS authorization_0_after_var0 - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles)) AND authorization_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $authorization_0_after_param3 IN $jwt.roles)) AND EXISTS { + MATCH (this0)-[:ITEM_TRANSACTED]->(authorization_0_after_this0:Transaction) + WHERE EXISTS { + MATCH (authorization_0_after_this0)-[:TRANSACTION]->(authorization_0_after_this1:Store) + WHERE ($jwt.store IS NOT NULL AND authorization_0_after_this1.id = $jwt.store) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this0 } CALL { WITH this0 - WITH * CALL { WITH this0 MATCH (this0)-[create_this0:ITEM_TRANSACTED]->(create_this1:Transaction) - OPTIONAL MATCH (create_this1)-[:TRANSACTION]->(create_this2:Store) - WITH *, count(create_this2) AS create_var3 - WITH * - WHERE (($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $create_param4 IN $jwt.roles)) AND (create_var3 <> 0 AND ($jwt.store IS NOT NULL AND create_this2.id = $jwt.store)))) + WHERE (($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param2 IN $jwt.roles)) OR ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles) OR ($jwt.roles IS NOT NULL AND $create_param4 IN $jwt.roles)) AND EXISTS { + MATCH (create_this1)-[:TRANSACTION]->(create_this2:Store) + WHERE ($jwt.store IS NOT NULL AND create_this2.id = $jwt.store) + })) + WITH DISTINCT create_this1 CALL { WITH create_this1 - MATCH (create_this1)-[create_this4:TRANSACTION]->(create_this5:Store) - WITH create_this5 { .name } AS create_this5 - RETURN head(collect(create_this5)) AS create_var6 + MATCH (create_this1)-[create_this3:TRANSACTION]->(create_this4:Store) + WITH DISTINCT create_this4 + WITH create_this4 { .name } AS create_this4 + RETURN collect(create_this4) AS create_var5 } - WITH create_this1 { .id, store: create_var6 } AS create_this1 - RETURN head(collect(create_this1)) AS create_var7 + WITH create_this1 { .id, store: create_var5 } AS create_this1 + RETURN collect(create_this1) AS create_var6 } - RETURN this0 { .name, transaction: create_var7 } AS create_var8 + RETURN this0 { .name, transaction: create_var6 } AS create_var7 } - RETURN [create_var8] AS data" + RETURN [create_var7] AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/issues/4223.test.ts b/packages/graphql/tests/tck/issues/4223.test.ts index b657f26d23..abd24ffc0a 100644 --- a/packages/graphql/tests/tck/issues/4223.test.ts +++ b/packages/graphql/tests/tck/issues/4223.test.ts @@ -28,34 +28,52 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { id: String roles: [String] } - type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique + type User + @authorization(validate: [{ where: { node: { userId: { eq: "$jwt.id" } } }, operations: [READ] }]) + @node { + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) } - type Tenant @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { + type Tenant + @authorization(validate: [{ where: { node: { admins: { some: { userId: { eq: "$jwt.id" } } } } } }]) + @node { id: ID! @id - settings: Settings! @relationship(type: "VEHICLECARD_OWNER", direction: IN) + settings: [Settings!]! @relationship(type: "VEHICLECARD_OWNER", direction: IN) admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN) } type Settings - @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) + @authorization( + validate: [ + { where: { node: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } } } + ] + ) @node { id: ID! @id - tenant: Tenant! @relationship(type: "HAS_SETTINGS", direction: IN) + tenant: [Tenant!]! @relationship(type: "HAS_SETTINGS", direction: IN) openingDays: [OpeningDay!]! @relationship(type: "VALID_OPENING_DAYS", direction: OUT) - myWorkspace: MyWorkspace! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: OUT) + myWorkspace: [MyWorkspace!]! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: OUT) name: String } type OpeningDay @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [ + { + where: { + node: { + settings: { + some: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } + } + } + } + } + ] ) { id: ID! @id - settings: Settings @relationship(type: "VALID_GARAGES", direction: IN) + settings: [Settings!]! @relationship(type: "VALID_GARAGES", direction: IN) open: [OpeningHoursInterval!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) } @@ -65,22 +83,42 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { validate: [ { where: { - node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } + node: { + openingDay: { + some: { + settings: { + some: { + tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } + } + } + } + } + } } } ] ) { name: String - openingDay: OpeningDay! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) + openingDay: [OpeningDay!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } type MyWorkspace @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [ + { + where: { + node: { + settings: { + some: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } + } + } + } + } + ] ) { - settings: Settings! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: IN) + settings: [Settings!]! @relationship(type: "HAS_WORKSPACE_SETTINGS", direction: IN) workspace: String updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) } @@ -152,7 +190,8 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Tenant) SET this0.id = randomUUID() WITH * @@ -166,103 +205,58 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { SET this0_settings0_node_openingDays0_node_open0_node.updatedBy = $resolvedCallbacks.this0_settings0_node_openingDays0_node_open0_node_updatedBy_getUserIDFromContext SET this0_settings0_node_openingDays0_node_open0_node.name = $this0_settings0_node_openingDays0_node_open0_node_name MERGE (this0_settings0_node_openingDays0_node)-[:HAS_OPEN_INTERVALS]->(this0_settings0_node_openingDays0_node_open0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique:HAS_OPEN_INTERVALS]-(:OpeningDay) - WITH count(this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningHoursInterval.openingDay required exactly once', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique_ignored - } MERGE (this0_settings0_node)-[:VALID_OPENING_DAYS]->(this0_settings0_node_openingDays0_node) WITH * - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[this0_settings0_node_openingDays0_node_settings_Settings_unique:VALID_GARAGES]-(:Settings) - WITH count(this0_settings0_node_openingDays0_node_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningDay.settings must be less than or equal to one', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_settings_Settings_unique_ignored - } - WITH * CREATE (this0_settings0_node_myWorkspace0_node:MyWorkspace) SET this0_settings0_node_myWorkspace0_node.updatedBy = $resolvedCallbacks.this0_settings0_node_myWorkspace0_node_updatedBy_getUserIDFromContext SET this0_settings0_node_myWorkspace0_node.workspace = $this0_settings0_node_myWorkspace0_node_workspace MERGE (this0_settings0_node)-[:HAS_WORKSPACE_SETTINGS]->(this0_settings0_node_myWorkspace0_node) - WITH * - CALL { - WITH this0_settings0_node_myWorkspace0_node - MATCH (this0_settings0_node_myWorkspace0_node)<-[this0_settings0_node_myWorkspace0_node_settings_Settings_unique:HAS_WORKSPACE_SETTINGS]-(:Settings) - WITH count(this0_settings0_node_myWorkspace0_node_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMyWorkspace.settings required exactly once', [0]) - RETURN c AS this0_settings0_node_myWorkspace0_node_settings_Settings_unique_ignored - } MERGE (this0)<-[:VEHICLECARD_OWNER]-(this0_settings0_node) WITH * - CALL { - WITH this0_settings0_node - MATCH (this0_settings0_node)<-[this0_settings0_node_tenant_Tenant_unique:HAS_SETTINGS]-(:Tenant) - WITH count(this0_settings0_node_tenant_Tenant_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSettings.tenant required exactly once', [0]) - RETURN c AS this0_settings0_node_tenant_Tenant_unique_ignored - } - CALL { - WITH this0_settings0_node - MATCH (this0_settings0_node)-[this0_settings0_node_myWorkspace_MyWorkspace_unique:HAS_WORKSPACE_SETTINGS]->(:MyWorkspace) - WITH count(this0_settings0_node_myWorkspace_MyWorkspace_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSettings.myWorkspace required exactly once', [0]) - RETURN c AS this0_settings0_node_myWorkspace_MyWorkspace_unique_ignored - } - WITH * CREATE (this0_admins0_node:User) SET this0_admins0_node.userId = $this0_admins0_node_userId MERGE (this0)<-[:ADMIN_IN]-(this0_admins0_node) WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_settings_Settings_unique:VEHICLECARD_OWNER]-(:Settings) - WITH count(this0_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDTenant.settings required exactly once', [0]) - RETURN c AS this0_settings_Settings_unique_ignored - } - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this1:OpeningDay) - CALL { - WITH authorization_0_0_0_0_0_0_0_0_0_0_after_this1 - MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this1)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this2:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this2)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this3:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_0_0_0_after_this3) AS authorization_0_0_0_0_0_0_0_0_0_0_after_var4 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_0_0_0_after_var4 <> 0 AND size([(authorization_0_0_0_0_0_0_0_0_0_0_after_this3)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this5:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_0_0_0_after_this5.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this2) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var6 + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this0:OpeningDay) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this0)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this1:Settings) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this1)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this2:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this3:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_0_0_0_after_this3.userId = $jwt.id) + } + } } - WITH * - WHERE authorization_0_0_0_0_0_0_0_0_0_0_after_var6 = true - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_after_this1:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_after_this1)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_after_this2) AS authorization_0_0_0_0_0_0_0_after_var3 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_after_var3 <> 0 AND size([(authorization_0_0_0_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_after_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_after_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_myWorkspace0_node - MATCH (this0_settings0_node_myWorkspace0_node)<-[:HAS_WORKSPACE_SETTINGS]-(authorization_0_0_0_0_1_0_0_after_this1:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_1_0_0_after_this1)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_1_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_1_0_0_after_this2) AS authorization_0_0_0_0_1_0_0_after_var3 - WITH * - WHERE (authorization_0_0_0_0_1_0_0_after_var3 <> 0 AND size([(authorization_0_0_0_0_1_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_1_0_0_after_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_1_0_0_after_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_1_0_0_after_this1) = 1 AS authorization_0_0_0_0_1_0_0_after_var0 - } - OPTIONAL MATCH (this0_settings0_node)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_after_this2) AS authorization_0_0_0_0_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_1_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_0_0_0_after_var0 <> 0 AND size([(authorization_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:ADMIN_IN]-(authorization_0_after_this0:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node_openingDays0_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_after_this0:Settings) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_after_this0)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_0_0_0_after_this1:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_0_0_0_after_this1)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_after_this2:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_after_this2.userId = $jwt.id) + } + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node_myWorkspace0_node)<-[:HAS_WORKSPACE_SETTINGS]-(authorization_0_0_0_0_1_0_0_after_this0:Settings) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_1_0_0_after_this0)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_1_0_0_after_this1:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_1_0_0_after_this1)<-[:ADMIN_IN]-(authorization_0_0_0_0_1_0_0_after_this2:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_1_0_0_after_this2.userId = $jwt.id) + } + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0_settings0_node)<-[:HAS_SETTINGS]-(authorization_0_0_0_0_after_this0:Tenant) + WHERE EXISTS { + MATCH (authorization_0_0_0_0_after_this0)<-[:ADMIN_IN]-(authorization_0_0_0_0_after_this1:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_after_this1.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)<-[:ADMIN_IN]-(authorization_0_after_this0:User) + WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this0.userId = $jwt.id) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this0 } CALL { @@ -271,6 +265,7 @@ describe("https://github.com/neo4j/graphql/issues/4223", () => { WITH this0 MATCH (this0)<-[create_this0:ADMIN_IN]-(create_this1:User) WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.id IS NOT NULL AND create_this1.userId = $jwt.id)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this1 WITH create_this1 { .userId } AS create_this1 RETURN collect(create_this1) AS create_var2 } diff --git a/packages/graphql/tests/tck/issues/4239.test.ts b/packages/graphql/tests/tck/issues/4239.test.ts index 8102283c04..c2d5394cea 100644 --- a/packages/graphql/tests/tck/issues/4239.test.ts +++ b/packages/graphql/tests/tck/issues/4239.test.ts @@ -45,7 +45,7 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { validate: [ { when: [BEFORE] - where: { node: { directorConnection_SOME: { node: { id_EQ: "$jwt.sub" } } } } + where: { node: { directorConnection: { some: { node: { id: { eq: "$jwt.sub" } } } } } } } ] ) { @@ -80,7 +80,8 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[this1:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -102,7 +103,7 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { type Movie @node @authorization( - validate: [{ when: [BEFORE], where: { node: { director_SOME: { id_EQ: "$jwt.sub" } } } }] + validate: [{ when: [BEFORE], where: { node: { director: { some: { id: { eq: "$jwt.sub" } } } } } }] ) { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) @@ -135,9 +136,13 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "4.4" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:DIRECTED]-(this0:Person) WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:DIRECTED]-(this0:Person) + WHERE ($jwt.sub IS NOT NULL AND this0.id = $jwt.sub) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" `); @@ -160,7 +165,7 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { validate: [ { when: [BEFORE] - where: { node: { directorConnection_SOME: { node: { id_EQ: "$jwt.sub" } } } } + where: { node: { directorConnection: { some: { node: { id: { eq: "$jwt.sub" } } } } } } } ] ) { @@ -195,7 +200,8 @@ describe("https://github.com/neo4j/graphql/issues/4239", () => { const result = await translateQuery(neoSchema, query, { token, neo4jVersion: "5.0" }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { MATCH (this)<-[this0:DIRECTED]-(this1:Person) diff --git a/packages/graphql/tests/tck/issues/4268.test.ts b/packages/graphql/tests/tck/issues/4268.test.ts index b7a5daf3d6..c86f5ce16c 100644 --- a/packages/graphql/tests/tck/issues/4268.test.ts +++ b/packages/graphql/tests/tck/issues/4268.test.ts @@ -34,7 +34,10 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { @node @authorization( validate: [ - { when: [BEFORE], where: { jwt: { OR: [{ roles_EQ: "admin" }, { roles_EQ: "super-admin" }] } } } + { + when: [BEFORE] + where: { jwt: { OR: [{ roles: { eq: "admin" } }, { roles: { eq: "super-admin" } }] } } + } ] ) { title: String @@ -55,7 +58,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $jwt.roles = $param2) OR ($jwt.roles IS NOT NULL AND $jwt.roles = $param3))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -94,8 +98,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { where: { jwt: { OR: [ - { OR: [{ roles: "admin" }, { roles: "super-admin" }] } - { OR: [{ roles: "user" }, { roles: "super-user" }] } + { OR: [{ roles: { eq: "admin" } }, { roles: { eq: "super-admin" } }] } + { OR: [{ roles: { eq: "user" } }, { roles: { eq: "super-user" } }] } ] } } @@ -125,7 +129,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((($jwt.roles IS NOT NULL AND $jwt.roles = $param2) OR ($jwt.roles IS NOT NULL AND $jwt.roles = $param3)) OR (($jwt.roles IS NOT NULL AND $jwt.roles = $param4) OR ($jwt.roles IS NOT NULL AND $jwt.roles = $param5)))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -161,7 +166,10 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { @node @authorization( validate: [ - { when: [BEFORE], where: { jwt: { AND: [{ roles_EQ: "admin" }, { roles_EQ: "super-admin" }] } } } + { + when: [BEFORE] + where: { jwt: { AND: [{ roles: { eq: "admin" } }, { roles: { eq: "super-admin" } }] } } + } ] ) { title: String @@ -187,7 +195,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (($jwt.roles IS NOT NULL AND $jwt.roles = $param2) AND ($jwt.roles IS NOT NULL AND $jwt.roles = $param3))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -226,8 +235,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { where: { jwt: { AND: [ - { AND: [{ roles_EQ: "admin" }, { roles_EQ: "super-admin" }] } - { AND: [{ roles_EQ: "user" }, { roles_EQ: "super-user" }] } + { AND: [{ roles: { eq: "admin" } }, { roles: { eq: "super-admin" } }] } + { AND: [{ roles: { eq: "user" } }, { roles: { eq: "super-user" } }] } ] } } @@ -257,7 +266,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((($jwt.roles IS NOT NULL AND $jwt.roles = $param2) AND ($jwt.roles IS NOT NULL AND $jwt.roles = $param3)) AND (($jwt.roles IS NOT NULL AND $jwt.roles = $param4) AND ($jwt.roles IS NOT NULL AND $jwt.roles = $param5)))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -290,7 +300,7 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { } type Movie - @authorization(validate: [{ when: [BEFORE], where: { jwt: { NOT: { roles_EQ: "admin" } } } }]) + @authorization(validate: [{ when: [BEFORE], where: { jwt: { NOT: { roles: { eq: "admin" } } } } }]) @node { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) @@ -315,7 +325,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND NOT ($jwt.roles IS NOT NULL AND $jwt.roles = $param2)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" @@ -346,7 +357,9 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { type Movie @node - @authorization(validate: [{ when: [BEFORE], where: { jwt: { NOT: { NOT: { roles_EQ: "admin" } } } } }]) { + @authorization( + validate: [{ when: [BEFORE], where: { jwt: { NOT: { NOT: { roles: { eq: "admin" } } } } } }] + ) { title: String director: [Person!]! @relationship(type: "DIRECTED", direction: IN) } @@ -370,7 +383,8 @@ describe("https://github.com/neo4j/graphql/issues/4268", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND NOT (NOT ($jwt.roles IS NOT NULL AND $jwt.roles = $param2))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .title } AS this" diff --git a/packages/graphql/tests/tck/issues/4287.test.ts b/packages/graphql/tests/tck/issues/4287.test.ts index be53d2d27f..b03b2d1526 100644 --- a/packages/graphql/tests/tck/issues/4287.test.ts +++ b/packages/graphql/tests/tck/issues/4287.test.ts @@ -49,7 +49,9 @@ describe("https://github.com/neo4j/graphql/issues/4287", () => { query { actors { actedInConnection( - where: { OR: [{ node: { title_EQ: "something" } }, { node: { title_EQ: "whatever" } }] } + where: { + OR: [{ node: { title: { eq: "something" } } }, { node: { title: { eq: "whatever" } } }] + } ) { edges { node { @@ -65,7 +67,8 @@ describe("https://github.com/neo4j/graphql/issues/4287", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/4292.test.ts b/packages/graphql/tests/tck/issues/4292.test.ts index bc5df5e613..d5f07c5f5f 100644 --- a/packages/graphql/tests/tck/issues/4292.test.ts +++ b/packages/graphql/tests/tck/issues/4292.test.ts @@ -25,8 +25,8 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { test("authorization subqueries should be wrapped in a Cypher.CALL", async () => { const typeDefs = /* GraphQL */ ` type User @node { - id: ID! @unique - email: String! @unique + id: ID! + email: String! name: String creator: [Group!]! @relationship(type: "CREATOR_OF", direction: OUT) admin: [Admin!]! @relationship(type: "IS_USER", direction: IN) @@ -36,10 +36,10 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { } type Group @node { - id: ID! @id @unique + id: ID! @id name: String members: [Person!]! @relationship(type: "MEMBER_OF", direction: IN) - creator: User! + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) @settable(onCreate: true, onUpdate: true) @@ -53,15 +53,25 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { validate: [ { operations: [CREATE] - where: { node: { group: { creator: { roles_INCLUDES: "plan:paid" } } } } + where: { + node: { group: { some: { creator: { some: { roles: { includes: "plan:paid" } } } } } } + } } { operations: [DELETE] where: { OR: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { group: { admins_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { creator: { id_EQ: "$jwt.uid" } } } } + { node: { creator: { some: { id: { eq: "$jwt.uid" } } } } } + { + node: { + group: { + some: { + admins: { some: { user: { some: { id: { eq: "$jwt.uid" } } } } } + } + } + } + } + { node: { group: { some: { creator: { some: { id: { eq: "$jwt.uid" } } } } } } } ] } } @@ -69,25 +79,43 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { operations: [READ, UPDATE] where: { OR: [ - { node: { creator: { id_EQ: "$jwt.uid" } } } - { node: { group: { admins_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { contributors_SOME: { user: { id_EQ: "$jwt.uid" } } } } } - { node: { group: { creator: { id_EQ: "$jwt.uid" } } } } + { node: { creator: { some: { id: { eq: "$jwt.uid" } } } } } + { + node: { + group: { + some: { + admins: { some: { user: { some: { id: { eq: "$jwt.uid" } } } } } + } + } + } + } + { + node: { + group: { + some: { + contributors: { + some: { user: { some: { id: { eq: "$jwt.uid" } } } } + } + } + } + } + } + { node: { group: { some: { creator: { some: { id: { eq: "$jwt.uid" } } } } } } } ] } } ] ) { - id: ID! @id @unique + id: ID! @id name: String! - creator: User! + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN, nestedOperations: [CONNECT]) @settable(onCreate: true, onUpdate: true) - group: Group! @relationship(type: "MEMBER_OF", direction: OUT) + group: [Group!]! @relationship(type: "MEMBER_OF", direction: OUT) partners: [Person!]! @relationship( type: "PARTNER_OF" - queryDirection: UNDIRECTED_ONLY + queryDirection: UNDIRECTED direction: OUT properties: "PartnerOf" ) @@ -107,32 +135,32 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { id: ID! email: String! name: String - creator: User! @declareRelationship - group: Group! @declareRelationship + creator: [User!]! @declareRelationship + group: [Group!]! @declareRelationship status: InviteeStatus! - user: User @declareRelationship + user: [User!]! @declareRelationship role: InviteeRole! } type Admin implements Invitee @node { - id: ID! @unique @id - group: Group! @relationship(type: "ADMIN_OF", direction: OUT) - creator: User! @relationship(type: "CREATOR_OF", direction: IN) + id: ID! @id + group: [Group!]! @relationship(type: "ADMIN_OF", direction: OUT) + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) email: String! name: String status: InviteeStatus! @default(value: INVITED) - user: User @relationship(type: "IS_USER", direction: OUT) + user: [User!]! @relationship(type: "IS_USER", direction: OUT) role: InviteeRole! @default(value: ADMIN) } type Contributor implements Invitee @node { - id: ID! @unique @id - group: Group! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) - creator: User! @relationship(type: "CREATOR_OF", direction: IN) + id: ID! @id + group: [Group!]! @relationship(type: "CONTRIBUTOR_TO", direction: OUT) + creator: [User!]! @relationship(type: "CREATOR_OF", direction: IN) email: String! name: String status: InviteeStatus! @default(value: INVITED) - user: User @relationship(type: "IS_USER", direction: OUT) + user: [User!]! @relationship(type: "IS_USER", direction: OUT) role: InviteeRole! @default(value: CONTRIBUTOR) } @@ -158,7 +186,7 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { const query = /* GraphQL */ ` query Groups { - groups(where: { id_EQ: "family_id_1" }) { + groups(where: { id: { eq: "family_id_1" } }) { id name members { @@ -182,78 +210,100 @@ describe("https://github.com/neo4j/graphql/issues/4292", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Group) + "CYPHER 5 + MATCH (this:Group) WHERE this.id = $param0 CALL { WITH this MATCH (this)<-[this0:MEMBER_OF]-(this1:Person) - OPTIONAL MATCH (this1)<-[:CREATOR_OF]-(this2:User) - WITH *, count(this2) AS var3 - OPTIONAL MATCH (this1)-[:MEMBER_OF]->(this4:Group) - WITH *, count(this4) AS var5 - OPTIONAL MATCH (this1)-[:MEMBER_OF]->(this6:Group) - WITH *, count(this6) AS var7 - CALL { - WITH this1 - MATCH (this1)-[:MEMBER_OF]->(this8:Group) - OPTIONAL MATCH (this8)<-[:CREATOR_OF]-(this9:User) - WITH *, count(this9) AS var10 - WITH * - WHERE (var10 <> 0 AND ($jwt.uid IS NOT NULL AND this9.id = $jwt.uid)) - RETURN count(this8) = 1 AS var11 - } + WITH DISTINCT this1 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((var3 <> 0 AND ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid)) OR (var5 <> 0 AND size([(this4)<-[:ADMIN_OF]-(this13:Admin) WHERE single(this12 IN [(this13)-[:IS_USER]->(this12:User) WHERE ($jwt.uid IS NOT NULL AND this12.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR (var7 <> 0 AND size([(this6)<-[:CONTRIBUTOR_TO]-(this15:Contributor) WHERE single(this14 IN [(this15)-[:IS_USER]->(this14:User) WHERE ($jwt.uid IS NOT NULL AND this14.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR var11 = true)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + MATCH (this1)<-[:CREATOR_OF]-(this2:User) + WHERE ($jwt.uid IS NOT NULL AND this2.id = $jwt.uid) + } OR EXISTS { + MATCH (this1)-[:MEMBER_OF]->(this3:Group) + WHERE EXISTS { + MATCH (this3)<-[:ADMIN_OF]-(this4:Admin) + WHERE EXISTS { + MATCH (this4)-[:IS_USER]->(this5:User) + WHERE ($jwt.uid IS NOT NULL AND this5.id = $jwt.uid) + } + } + } OR EXISTS { + MATCH (this1)-[:MEMBER_OF]->(this6:Group) + WHERE EXISTS { + MATCH (this6)<-[:CONTRIBUTOR_TO]-(this7:Contributor) + WHERE EXISTS { + MATCH (this7)-[:IS_USER]->(this8:User) + WHERE ($jwt.uid IS NOT NULL AND this8.id = $jwt.uid) + } + } + } OR EXISTS { + MATCH (this1)-[:MEMBER_OF]->(this9:Group) + WHERE EXISTS { + MATCH (this9)<-[:CREATOR_OF]-(this10:User) + WHERE ($jwt.uid IS NOT NULL AND this10.id = $jwt.uid) + } + })), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this1 - MATCH (this1)-[this16:PARTNER_OF]-(this17:Person) - OPTIONAL MATCH (this17)<-[:CREATOR_OF]-(this18:User) - WITH *, count(this18) AS var19 - OPTIONAL MATCH (this17)-[:MEMBER_OF]->(this20:Group) - WITH *, count(this20) AS var21 - OPTIONAL MATCH (this17)-[:MEMBER_OF]->(this22:Group) - WITH *, count(this22) AS var23 - OPTIONAL MATCH (this17)-[:MEMBER_OF]->(this24:Group) - WITH *, count(this24) AS var25 - WITH * - CALL { - WITH this17 - MATCH (this17)-[:MEMBER_OF]->(this26:Group) - OPTIONAL MATCH (this26)<-[:CREATOR_OF]-(this27:User) - WITH *, count(this27) AS var28 - WITH * - WHERE (var28 <> 0 AND ($jwt.uid IS NOT NULL AND this27.id = $jwt.uid)) - RETURN count(this26) = 1 AS var29 - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ((var19 <> 0 AND ($jwt.uid IS NOT NULL AND this18.id = $jwt.uid)) OR (var21 <> 0 AND size([(this20)<-[:ADMIN_OF]-(this31:Admin) WHERE single(this30 IN [(this31)-[:IS_USER]->(this30:User) WHERE ($jwt.uid IS NOT NULL AND this30.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR (var23 <> 0 AND size([(this22)<-[:CONTRIBUTOR_TO]-(this33:Contributor) WHERE single(this32 IN [(this33)-[:IS_USER]->(this32:User) WHERE ($jwt.uid IS NOT NULL AND this32.id = $jwt.uid) | 1] WHERE true) | 1]) > 0) OR var29 = true)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this17, relationship: this16 }) AS edges + MATCH (this1)-[this11:PARTNER_OF]-(this12:Person) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (EXISTS { + MATCH (this12)<-[:CREATOR_OF]-(this13:User) + WHERE ($jwt.uid IS NOT NULL AND this13.id = $jwt.uid) + } OR EXISTS { + MATCH (this12)-[:MEMBER_OF]->(this14:Group) + WHERE EXISTS { + MATCH (this14)<-[:ADMIN_OF]-(this15:Admin) + WHERE EXISTS { + MATCH (this15)-[:IS_USER]->(this16:User) + WHERE ($jwt.uid IS NOT NULL AND this16.id = $jwt.uid) + } + } + } OR EXISTS { + MATCH (this12)-[:MEMBER_OF]->(this17:Group) + WHERE EXISTS { + MATCH (this17)<-[:CONTRIBUTOR_TO]-(this18:Contributor) + WHERE EXISTS { + MATCH (this18)-[:IS_USER]->(this19:User) + WHERE ($jwt.uid IS NOT NULL AND this19.id = $jwt.uid) + } + } + } OR EXISTS { + MATCH (this12)-[:MEMBER_OF]->(this20:Group) + WHERE EXISTS { + MATCH (this20)<-[:CREATOR_OF]-(this21:User) + WHERE ($jwt.uid IS NOT NULL AND this21.id = $jwt.uid) + } + })), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH collect({ node: this12, relationship: this11 }) AS edges WITH edges, size(edges) AS totalCount CALL { WITH edges UNWIND edges AS edge - WITH edge.node AS this17, edge.relationship AS this16 - RETURN collect({ properties: { active: this16.active, firstDay: this16.firstDay, lastDay: this16.lastDay, __resolveType: \\"PartnerOf\\" }, node: { __id: id(this17), __resolveType: \\"Person\\" } }) AS var34 + WITH edge.node AS this12, edge.relationship AS this11 + RETURN collect({ properties: { active: this11.active, firstDay: this11.firstDay, lastDay: this11.lastDay, __resolveType: \\"PartnerOf\\" }, node: { __id: id(this12), __resolveType: \\"Person\\" } }) AS var22 } - RETURN { edges: var34, totalCount: totalCount } AS var35 + RETURN { edges: var22, totalCount: totalCount } AS var23 } - WITH this1 { .id, .name, partnersConnection: var35 } AS this1 - RETURN collect(this1) AS var36 + WITH this1 { .id, .name, partnersConnection: var23 } AS this1 + RETURN collect(this1) AS var24 } - RETURN this { .id, .name, members: var36 } AS this" + RETURN this { .id, .name, members: var24 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": \\"family_id_1\\", + \\"isAuthenticated\\": true, \\"jwt\\": { \\"roles\\": [ \\"admin\\" ], \\"id\\": \\"something\\", \\"email\\": \\"something\\" - }, - \\"isAuthenticated\\": true + } }" `); }); diff --git a/packages/graphql/tests/tck/issues/433.test.ts b/packages/graphql/tests/tck/issues/433.test.ts index cbdf133412..f1702cc175 100644 --- a/packages/graphql/tests/tck/issues/433.test.ts +++ b/packages/graphql/tests/tck/issues/433.test.ts @@ -61,7 +61,8 @@ describe("#413", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:ACTED_IN]->(this1:Person) diff --git a/packages/graphql/tests/tck/issues/4405.test.ts b/packages/graphql/tests/tck/issues/4405.test.ts index 8d63dea1ab..2900df87ce 100644 --- a/packages/graphql/tests/tck/issues/4405.test.ts +++ b/packages/graphql/tests/tck/issues/4405.test.ts @@ -35,7 +35,7 @@ describe("https://github.com/neo4j/graphql/issues/4405", () => { { when: [BEFORE] operations: [READ] - where: { node: { actedInConnection_SOME: { node: { title_IN: ["Matrix"] } } } } + where: { node: { actedInConnection: { some: { node: { title: { in: ["Matrix"] } } } } } } } ] ) { @@ -58,7 +58,8 @@ describe("https://github.com/neo4j/graphql/issues/4405", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)-[this1:ACTED_IN]->(this0:Movie) WHERE ($param1 IS NOT NULL AND this0.title IN $param1) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .name } AS this" @@ -89,9 +90,14 @@ describe("https://github.com/neo4j/graphql/issues/4405", () => { operations: [READ] where: { node: { - actedInConnection_SOME: { - node: { - OR: [{ title_IN: ["Matrix"] }, { title_IN: ["Forrest Gump", "Top Gun"] }] + actedInConnection: { + some: { + node: { + OR: [ + { title: { in: ["Matrix"] } } + { title: { in: ["Forrest Gump", "Top Gun"] } } + ] + } } } } @@ -118,7 +124,8 @@ describe("https://github.com/neo4j/graphql/issues/4405", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)-[this1:ACTED_IN]->(this0:Movie) WHERE (($param1 IS NOT NULL AND this0.title IN $param1) OR ($param2 IS NOT NULL AND this0.title IN $param2)) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN this { .name } AS this" diff --git a/packages/graphql/tests/tck/issues/4429.test.ts b/packages/graphql/tests/tck/issues/4429.test.ts deleted file mode 100644 index afdf3e8247..0000000000 --- a/packages/graphql/tests/tck/issues/4429.test.ts +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4429", () => { - let neoSchema: Neo4jGraphQL; - - const typeDefs = /* GraphQL */ ` - type JWT @jwt { - id: String - roles: [String] - } - type User @authorization(validate: [{ where: { node: { userId_EQ: "$jwt.id" } }, operations: [READ] }]) @node { - userId: String! @unique - adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT) - } - - type Tenant @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) @node { - id: ID! @id - admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN) - settings: Settings @relationship(type: "VEHICLECARD_OWNER", direction: IN) - } - - type Settings - @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) - @node { - id: ID! @id - openingDays: [OpeningDay!]! @relationship(type: "VALID_GARAGES", direction: OUT) - tenant: Tenant! @relationship(type: "VEHICLECARD_OWNER", direction: OUT) # <--- this line - } - - type OpeningDay - @node - @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] - ) { - id: ID! @id - settings: Settings @relationship(type: "VALID_GARAGES", direction: IN) - open: [OpeningHoursInterval!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT) - } - type OpeningHoursInterval - @node - @authorization( - validate: [ - { - where: { - node: { openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } - } - } - ] - ) { - name: String - openingDay: OpeningDay! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN) - createdAt: DateTime! @timestamp(operations: [CREATE]) - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) - updatedBy: String @populatedBy(callback: "getUserIDFromContext", operations: [CREATE, UPDATE]) - } - `; - - beforeAll(() => { - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - populatedBy: { - callbacks: { - getUserIDFromContext: () => "hi", - }, - }, - }, - }); - }); - - test("should include checks for auth jwt param is not null", async () => { - const query = /* GraphQL */ ` - mutation addTenant($input: [TenantCreateInput!]!) { - createTenants(input: $input) { - tenants { - id - admins { - userId - } - settings { - openingDays { - open { - name - } - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - variableValues: { - input: { - settings: { - create: { - node: { - openingDays: { - create: [ - { - node: { - open: { - create: [ - { - node: { - name: "hi", - }, - }, - ], - }, - }, - }, - { - node: { - open: { - create: [ - { - node: { - name: "hi", - }, - }, - { - node: { - name: "hi", - }, - }, - ], - }, - }, - }, - ], - }, - }, - }, - }, - }, - }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Tenant) - SET this0.id = randomUUID() - WITH * - CREATE (this0_settings0_node:Settings) - SET this0_settings0_node.id = randomUUID() - WITH * - CREATE (this0_settings0_node_openingDays0_node:OpeningDay) - SET this0_settings0_node_openingDays0_node.id = randomUUID() - WITH * - CREATE (this0_settings0_node_openingDays0_node_open0_node:OpeningHoursInterval) - SET this0_settings0_node_openingDays0_node_open0_node.createdAt = datetime() - SET this0_settings0_node_openingDays0_node_open0_node.updatedAt = datetime() - SET this0_settings0_node_openingDays0_node_open0_node.updatedBy = $resolvedCallbacks.this0_settings0_node_openingDays0_node_open0_node_updatedBy_getUserIDFromContext - SET this0_settings0_node_openingDays0_node_open0_node.name = $this0_settings0_node_openingDays0_node_open0_node_name - MERGE (this0_settings0_node_openingDays0_node)-[:HAS_OPEN_INTERVALS]->(this0_settings0_node_openingDays0_node_open0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique:HAS_OPEN_INTERVALS]-(:OpeningDay) - WITH count(this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningHoursInterval.openingDay required exactly once', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_open0_node_openingDay_OpeningDay_unique_ignored - } - MERGE (this0_settings0_node)-[:VALID_GARAGES]->(this0_settings0_node_openingDays0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[this0_settings0_node_openingDays0_node_settings_Settings_unique:VALID_GARAGES]-(:Settings) - WITH count(this0_settings0_node_openingDays0_node_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningDay.settings must be less than or equal to one', [0]) - RETURN c AS this0_settings0_node_openingDays0_node_settings_Settings_unique_ignored - } - WITH * - CREATE (this0_settings0_node_openingDays1_node:OpeningDay) - SET this0_settings0_node_openingDays1_node.id = randomUUID() - WITH * - CREATE (this0_settings0_node_openingDays1_node_open0_node:OpeningHoursInterval) - SET this0_settings0_node_openingDays1_node_open0_node.createdAt = datetime() - SET this0_settings0_node_openingDays1_node_open0_node.updatedAt = datetime() - SET this0_settings0_node_openingDays1_node_open0_node.updatedBy = $resolvedCallbacks.this0_settings0_node_openingDays1_node_open0_node_updatedBy_getUserIDFromContext - SET this0_settings0_node_openingDays1_node_open0_node.name = $this0_settings0_node_openingDays1_node_open0_node_name - MERGE (this0_settings0_node_openingDays1_node)-[:HAS_OPEN_INTERVALS]->(this0_settings0_node_openingDays1_node_open0_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays1_node_open0_node - MATCH (this0_settings0_node_openingDays1_node_open0_node)<-[this0_settings0_node_openingDays1_node_open0_node_openingDay_OpeningDay_unique:HAS_OPEN_INTERVALS]-(:OpeningDay) - WITH count(this0_settings0_node_openingDays1_node_open0_node_openingDay_OpeningDay_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningHoursInterval.openingDay required exactly once', [0]) - RETURN c AS this0_settings0_node_openingDays1_node_open0_node_openingDay_OpeningDay_unique_ignored - } - WITH * - CREATE (this0_settings0_node_openingDays1_node_open1_node:OpeningHoursInterval) - SET this0_settings0_node_openingDays1_node_open1_node.createdAt = datetime() - SET this0_settings0_node_openingDays1_node_open1_node.updatedAt = datetime() - SET this0_settings0_node_openingDays1_node_open1_node.updatedBy = $resolvedCallbacks.this0_settings0_node_openingDays1_node_open1_node_updatedBy_getUserIDFromContext - SET this0_settings0_node_openingDays1_node_open1_node.name = $this0_settings0_node_openingDays1_node_open1_node_name - MERGE (this0_settings0_node_openingDays1_node)-[:HAS_OPEN_INTERVALS]->(this0_settings0_node_openingDays1_node_open1_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays1_node_open1_node - MATCH (this0_settings0_node_openingDays1_node_open1_node)<-[this0_settings0_node_openingDays1_node_open1_node_openingDay_OpeningDay_unique:HAS_OPEN_INTERVALS]-(:OpeningDay) - WITH count(this0_settings0_node_openingDays1_node_open1_node_openingDay_OpeningDay_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningHoursInterval.openingDay required exactly once', [0]) - RETURN c AS this0_settings0_node_openingDays1_node_open1_node_openingDay_OpeningDay_unique_ignored - } - MERGE (this0_settings0_node)-[:VALID_GARAGES]->(this0_settings0_node_openingDays1_node) - WITH * - CALL { - WITH this0_settings0_node_openingDays1_node - MATCH (this0_settings0_node_openingDays1_node)<-[this0_settings0_node_openingDays1_node_settings_Settings_unique:VALID_GARAGES]-(:Settings) - WITH count(this0_settings0_node_openingDays1_node_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDOpeningDay.settings must be less than or equal to one', [0]) - RETURN c AS this0_settings0_node_openingDays1_node_settings_Settings_unique_ignored - } - MERGE (this0)<-[:VEHICLECARD_OWNER]-(this0_settings0_node) - WITH * - CALL { - WITH this0_settings0_node - MATCH (this0_settings0_node)-[this0_settings0_node_tenant_Tenant_unique:VEHICLECARD_OWNER]->(:Tenant) - WITH count(this0_settings0_node_tenant_Tenant_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSettings.tenant required exactly once', [0]) - RETURN c AS this0_settings0_node_tenant_Tenant_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_settings_Settings_unique:VEHICLECARD_OWNER]-(:Settings) - WITH count(this0_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDTenant.settings must be less than or equal to one', [0]) - RETURN c AS this0_settings_Settings_unique_ignored - } - WITH * - CALL { - WITH this0_settings0_node_openingDays0_node_open0_node - MATCH (this0_settings0_node_openingDays0_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this1:OpeningDay) - CALL { - WITH authorization_0_0_0_0_0_0_0_0_0_0_after_this1 - MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this1)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this2:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_0_0_0_after_this2)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_0_0_0_0_0_0_after_this3:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_0_0_0_after_this3) AS authorization_0_0_0_0_0_0_0_0_0_0_after_var4 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_0_0_0_after_var4 <> 0 AND size([(authorization_0_0_0_0_0_0_0_0_0_0_after_this3)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_0_0_0_after_this5:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_0_0_0_after_this5.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this2) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var6 - } - WITH * - WHERE authorization_0_0_0_0_0_0_0_0_0_0_after_var6 = true - RETURN count(authorization_0_0_0_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays0_node - MATCH (this0_settings0_node_openingDays0_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_0_0_after_this1:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_0_0_after_this1)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_0_0_0_after_this2) AS authorization_0_0_0_0_0_0_0_after_var3 - WITH * - WHERE (authorization_0_0_0_0_0_0_0_after_var3 <> 0 AND size([(authorization_0_0_0_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_0_0_after_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_0_0_after_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays1_node_open0_node - MATCH (this0_settings0_node_openingDays1_node_open0_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_1_0_0_0_0_after_this1:OpeningDay) - CALL { - WITH authorization_0_0_0_0_0_1_0_0_0_0_after_this1 - MATCH (authorization_0_0_0_0_0_1_0_0_0_0_after_this1)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_1_0_0_0_0_after_this2:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_1_0_0_0_0_after_this2)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_0_1_0_0_0_0_after_this3:Tenant) - WITH *, count(authorization_0_0_0_0_0_1_0_0_0_0_after_this3) AS authorization_0_0_0_0_0_1_0_0_0_0_after_var4 - WITH * - WHERE (authorization_0_0_0_0_0_1_0_0_0_0_after_var4 <> 0 AND size([(authorization_0_0_0_0_0_1_0_0_0_0_after_this3)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_1_0_0_0_0_after_this5:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_1_0_0_0_0_after_this5.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_1_0_0_0_0_after_this2) = 1 AS authorization_0_0_0_0_0_1_0_0_0_0_after_var6 - } - WITH * - WHERE authorization_0_0_0_0_0_1_0_0_0_0_after_var6 = true - RETURN count(authorization_0_0_0_0_0_1_0_0_0_0_after_this1) = 1 AS authorization_0_0_0_0_0_1_0_0_0_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays1_node_open1_node - MATCH (this0_settings0_node_openingDays1_node_open1_node)<-[:HAS_OPEN_INTERVALS]-(authorization_0_0_0_0_0_1_0_0_1_0_after_this1:OpeningDay) - CALL { - WITH authorization_0_0_0_0_0_1_0_0_1_0_after_this1 - MATCH (authorization_0_0_0_0_0_1_0_0_1_0_after_this1)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_1_0_0_1_0_after_this2:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_1_0_0_1_0_after_this2)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_0_1_0_0_1_0_after_this3:Tenant) - WITH *, count(authorization_0_0_0_0_0_1_0_0_1_0_after_this3) AS authorization_0_0_0_0_0_1_0_0_1_0_after_var4 - WITH * - WHERE (authorization_0_0_0_0_0_1_0_0_1_0_after_var4 <> 0 AND size([(authorization_0_0_0_0_0_1_0_0_1_0_after_this3)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_1_0_0_1_0_after_this5:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_1_0_0_1_0_after_this5.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_1_0_0_1_0_after_this2) = 1 AS authorization_0_0_0_0_0_1_0_0_1_0_after_var6 - } - WITH * - WHERE authorization_0_0_0_0_0_1_0_0_1_0_after_var6 = true - RETURN count(authorization_0_0_0_0_0_1_0_0_1_0_after_this1) = 1 AS authorization_0_0_0_0_0_1_0_0_1_0_after_var0 - } - CALL { - WITH this0_settings0_node_openingDays1_node - MATCH (this0_settings0_node_openingDays1_node)<-[:VALID_GARAGES]-(authorization_0_0_0_0_0_1_0_after_this1:Settings) - OPTIONAL MATCH (authorization_0_0_0_0_0_1_0_after_this1)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_0_1_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_0_1_0_after_this2) AS authorization_0_0_0_0_0_1_0_after_var3 - WITH * - WHERE (authorization_0_0_0_0_0_1_0_after_var3 <> 0 AND size([(authorization_0_0_0_0_0_1_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_0_1_0_after_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_0_1_0_after_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_0_0_0_0_0_1_0_after_this1) = 1 AS authorization_0_0_0_0_0_1_0_after_var0 - } - OPTIONAL MATCH (this0_settings0_node)-[:VEHICLECARD_OWNER]->(authorization_0_0_0_0_after_this2:Tenant) - WITH *, count(authorization_0_0_0_0_after_this2) AS authorization_0_0_0_0_after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_1_0_0_0_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_1_0_0_1_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_0_0_0_0_0_1_0_after_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_0_0_0_0_after_var0 <> 0 AND size([(authorization_0_0_0_0_after_this2)<-[:ADMIN_IN]-(authorization_0_0_0_0_after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_0_0_0_after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this0)<-[:ADMIN_IN]-(authorization_0_after_this0:User) WHERE ($jwt.id IS NOT NULL AND authorization_0_after_this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)<-[create_this0:ADMIN_IN]-(create_this1:User) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.id IS NOT NULL AND create_this1.userId = $jwt.id)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this1 { .userId } AS create_this1 - RETURN collect(create_this1) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:VEHICLECARD_OWNER]-(create_this4:Settings) - OPTIONAL MATCH (create_this4)-[:VEHICLECARD_OWNER]->(create_this5:Tenant) - WITH *, count(create_this5) AS create_var6 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (create_var6 <> 0 AND size([(create_this5)<-[:ADMIN_IN]-(create_this7:User) WHERE ($jwt.id IS NOT NULL AND create_this7.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH create_this4 - MATCH (create_this4)-[create_this8:VALID_GARAGES]->(create_this9:OpeningDay) - CALL { - WITH create_this9 - MATCH (create_this9)<-[:VALID_GARAGES]-(create_this10:Settings) - OPTIONAL MATCH (create_this10)-[:VEHICLECARD_OWNER]->(create_this11:Tenant) - WITH *, count(create_this11) AS create_var12 - WITH * - WHERE (create_var12 <> 0 AND size([(create_this11)<-[:ADMIN_IN]-(create_this13:User) WHERE ($jwt.id IS NOT NULL AND create_this13.userId = $jwt.id) | 1]) > 0) - RETURN count(create_this10) = 1 AS create_var14 - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND create_var14 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH create_this9 - MATCH (create_this9)-[create_this15:HAS_OPEN_INTERVALS]->(create_this16:OpeningHoursInterval) - CALL { - WITH create_this16 - MATCH (create_this16)<-[:HAS_OPEN_INTERVALS]-(create_this17:OpeningDay) - CALL { - WITH create_this17 - MATCH (create_this17)<-[:VALID_GARAGES]-(create_this18:Settings) - OPTIONAL MATCH (create_this18)-[:VEHICLECARD_OWNER]->(create_this19:Tenant) - WITH *, count(create_this19) AS create_var20 - WITH * - WHERE (create_var20 <> 0 AND size([(create_this19)<-[:ADMIN_IN]-(create_this21:User) WHERE ($jwt.id IS NOT NULL AND create_this21.userId = $jwt.id) | 1]) > 0) - RETURN count(create_this18) = 1 AS create_var22 - } - WITH * - WHERE create_var22 = true - RETURN count(create_this17) = 1 AS create_var23 - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND create_var23 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this16 { .name } AS create_this16 - RETURN collect(create_this16) AS create_var24 - } - WITH create_this9 { open: create_var24 } AS create_this9 - RETURN collect(create_this9) AS create_var25 - } - WITH create_this4 { openingDays: create_var25 } AS create_this4 - RETURN head(collect(create_this4)) AS create_var26 - } - RETURN this0 { .id, admins: create_var2, settings: create_var26 } AS create_var27 - } - RETURN [create_var27] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": false, - \\"jwt\\": {}, - \\"this0_settings0_node_openingDays0_node_open0_node_name\\": \\"hi\\", - \\"this0_settings0_node_openingDays1_node_open0_node_name\\": \\"hi\\", - \\"this0_settings0_node_openingDays1_node_open1_node_name\\": \\"hi\\", - \\"resolvedCallbacks\\": { - \\"this0_settings0_node_openingDays0_node_open0_node_updatedBy_getUserIDFromContext\\": \\"hi\\", - \\"this0_settings0_node_openingDays1_node_open0_node_updatedBy_getUserIDFromContext\\": \\"hi\\", - \\"this0_settings0_node_openingDays1_node_open1_node_updatedBy_getUserIDFromContext\\": \\"hi\\" - } - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4432.test.ts b/packages/graphql/tests/tck/issues/4432.test.ts index a53139c0c2..30fec79242 100644 --- a/packages/graphql/tests/tck/issues/4432.test.ts +++ b/packages/graphql/tests/tck/issues/4432.test.ts @@ -69,7 +69,8 @@ describe("https://github.com/neo4j/graphql/issues/4532", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Inventory) + "CYPHER 5 + MATCH (this:Inventory) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/4450.test.ts b/packages/graphql/tests/tck/issues/4450.test.ts deleted file mode 100644 index 53033bd384..0000000000 --- a/packages/graphql/tests/tck/issues/4450.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4450", () => { - test("filtering through a connection to a many-to-1 relationship should work", async () => { - const typeDefs = /* GraphQL */ ` - type Actor @node { - name: String - scene: [Scene!]! @relationship(type: "IN_SCENE", properties: "ActorScene", direction: OUT) - } - - type Scene @node { - number: Int - actors: [Actor!]! @relationship(type: "IN_SCENE", properties: "ActorScene", direction: IN) - location: Location! @relationship(type: "AT_LOCATION", direction: OUT) - } - - type Location @node { - city: String - scenes: [Scene!]! @relationship(type: "AT_LOCATION", direction: IN) - } - - type ActorScene @relationshipProperties { - cut: Boolean - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const query = /* GraphQL */ ` - query { - actors( - where: { sceneConnection_SOME: { edge: { cut_EQ: true }, node: { location: { city_EQ: "test" } } } } - ) { - name - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - CALL { - WITH this - MATCH (this)-[this0:IN_SCENE]->(this1:Scene) - OPTIONAL MATCH (this1)-[:AT_LOCATION]->(this2:Location) - WITH *, count(this2) AS var3 - WITH * - WHERE ((var3 <> 0 AND this2.city = $param0) AND this0.cut = $param1) - RETURN count(this1) > 0 AS var4 - } - WITH * - WHERE var4 = true - RETURN this { .name } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"test\\", - \\"param1\\": true - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4477.test.ts b/packages/graphql/tests/tck/issues/4477.test.ts deleted file mode 100644 index 2b947a2316..0000000000 --- a/packages/graphql/tests/tck/issues/4477.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4477", () => { - test("filtering by count on an aggregate should work", async () => { - const typeDefs = /* GraphQL */ ` - type Brand @node { - services: [Service!]! @relationship(type: "HAS_SERVICE", direction: OUT) - name: String! - } - - type Collection @node { - services: [Service!]! @relationship(type: "HAS_SERVICE", direction: OUT) - } - - type Service @node { - collection: Collection @relationship(type: "HAS_SERVICE", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const query = /* GraphQL */ ` - query { - brands { - name - services(where: { collectionAggregate: { count_EQ: 0 } }) { - collectionAggregate { - count - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Brand) - CALL { - WITH this - MATCH (this)-[this0:HAS_SERVICE]->(this1:Service) - CALL { - WITH this1 - MATCH (this1)<-[this2:HAS_SERVICE]-(this3:Collection) - RETURN count(this3) = $param0 AS var4 - } - WITH * - WHERE var4 = true - CALL { - WITH this1 - MATCH (this1)<-[this5:HAS_SERVICE]-(this6:Collection) - RETURN count(this6) AS var7 - } - WITH this1 { collectionAggregate: { count: var7 } } AS this1 - RETURN collect(this1) AS var8 - } - RETURN this { .name, services: var8 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": { - \\"low\\": 0, - \\"high\\": 0 - } - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4583.test.ts b/packages/graphql/tests/tck/issues/4583.test.ts index e4959af9c9..c00d3b517c 100644 --- a/packages/graphql/tests/tck/issues/4583.test.ts +++ b/packages/graphql/tests/tck/issues/4583.test.ts @@ -26,7 +26,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { typeDefs = /* GraphQL */ ` type Episode @node { runtime: Int! - series: Series! @relationship(type: "HAS_EPISODE", direction: IN) + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -73,7 +73,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { actedIn: { connect: { edge: { screenTime: 10 } - where: { node: { title_EQ: "movieTitle", typename_IN: [Movie] } } + where: { node: { title: { eq: "movieTitle" }, typename: [Movie] } } } } } @@ -88,7 +88,8 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { const result = await translateQuery(neoSchema, mutation); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * @@ -103,7 +104,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect0_node - MERGE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) + CREATE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) SET this0_actedIn_connect0_relationship.screenTime = $this0_actedIn_connect0_relationship_screenTime } } @@ -121,7 +122,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect1_node - MERGE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) + CREATE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) SET this0_actedIn_connect1_relationship.screenTime = $this0_actedIn_connect1_relationship_screenTime } } @@ -166,7 +167,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { actedIn: { connect: { edge: { screenTime: 10 } - where: { node: { OR: [{ title_EQ: "movieTitle" }, { typename_IN: [Movie] }] } } + where: { node: { OR: [{ title: { eq: "movieTitle" } }, { typename: [Movie] }] } } } } } @@ -181,7 +182,8 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { const result = await translateQuery(neoSchema, mutation); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * @@ -196,7 +198,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect0_node - MERGE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) + CREATE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) SET this0_actedIn_connect0_relationship.screenTime = $this0_actedIn_connect0_relationship_screenTime } } @@ -214,7 +216,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect1_node - MERGE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) + CREATE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) SET this0_actedIn_connect1_relationship.screenTime = $this0_actedIn_connect1_relationship_screenTime } } @@ -259,11 +261,11 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { actedIn: { connect: { edge: { screenTime: 10 } - where: { node: { title_EQ: "movieTitle", typename_IN: [Movie] } } + where: { node: { title: { eq: "movieTitle" }, typename: [Movie] } } connect: { actors: { edge: { StarredIn: { episodeNr: 10 }, ActedIn: { screenTime: 25 } } - where: { node: { name_EQ: "Second Actor" } } + where: { node: { name: { eq: "Second Actor" } } } } } } @@ -280,7 +282,8 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { const result = await translateQuery(neoSchema, mutation); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * @@ -295,7 +298,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect0_node - MERGE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) + CREATE (this0)-[this0_actedIn_connect0_relationship:ACTED_IN]->(this0_actedIn_connect0_node) SET this0_actedIn_connect0_relationship.screenTime = $this0_actedIn_connect0_relationship_screenTime } } @@ -311,7 +314,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_actedIn_connect0_node UNWIND connectedNodes as this0_actedIn_connect0_node_actors0_node - MERGE (this0_actedIn_connect0_node)<-[this0_actedIn_connect0_node_actors0_relationship:ACTED_IN]-(this0_actedIn_connect0_node_actors0_node) + CREATE (this0_actedIn_connect0_node)<-[this0_actedIn_connect0_node_actors0_relationship:ACTED_IN]-(this0_actedIn_connect0_node_actors0_node) SET this0_actedIn_connect0_node_actors0_relationship.screenTime = $this0_actedIn_connect0_node_actors0_relationship_ActedIn_screenTime } } @@ -331,7 +334,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actedIn_connect1_node - MERGE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) + CREATE (this0)-[this0_actedIn_connect1_relationship:ACTED_IN]->(this0_actedIn_connect1_node) SET this0_actedIn_connect1_relationship.screenTime = $this0_actedIn_connect1_relationship_screenTime } } @@ -347,7 +350,7 @@ describe("https://github.com/neo4j/graphql/issues/4583", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_actedIn_connect1_node UNWIND connectedNodes as this0_actedIn_connect1_node_actors0_node - MERGE (this0_actedIn_connect1_node)<-[this0_actedIn_connect1_node_actors0_relationship:ACTED_IN]-(this0_actedIn_connect1_node_actors0_node) + CREATE (this0_actedIn_connect1_node)<-[this0_actedIn_connect1_node_actors0_relationship:ACTED_IN]-(this0_actedIn_connect1_node_actors0_node) SET this0_actedIn_connect1_node_actors0_relationship.episodeNr = $this0_actedIn_connect1_node_actors0_relationship_StarredIn_episodeNr } } diff --git a/packages/graphql/tests/tck/issues/4667.test.ts b/packages/graphql/tests/tck/issues/4667.test.ts deleted file mode 100644 index 4d2be4959c..0000000000 --- a/packages/graphql/tests/tck/issues/4667.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/4667", () => { - test("when passed null as an argument of a relationship filter should check that a relationship does not exist", async () => { - const typeDefs = /* GraphQL */ ` - type MyThing @node { - id: ID! @id - stuff: MyStuff @relationship(type: "THE_STUFF", direction: OUT) - } - - type MyStuff @node { - id: ID! @id - thing: MyThing @relationship(type: "THE_STUFF", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const query = /* GraphQL */ ` - query { - myThings(where: { stuff: null }) { - id - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:MyThing) - WHERE NOT (EXISTS { - MATCH (this)-[:THE_STUFF]->(this0:MyStuff) - }) - RETURN this { .id } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/issues/4704.test.ts b/packages/graphql/tests/tck/issues/4704.test.ts index a7c4375097..250f16da8d 100644 --- a/packages/graphql/tests/tck/issues/4704.test.ts +++ b/packages/graphql/tests/tck/issues/4704.test.ts @@ -69,7 +69,9 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { { actors( where: { - actedInConnection_ALL: { node: { actorsConnection_ALL: { node: { name_EQ: "Keanu Reeves" } } } } + actedInConnection: { + all: { node: { actorsConnection: { all: { node: { name: { eq: "Keanu Reeves" } } } } } } + } } ) { actedIn { @@ -82,7 +84,8 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE ((EXISTS { MATCH (this)-[this0:ACTED_IN]->(this1:Movie) WHERE (EXISTS { @@ -152,8 +155,10 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { { actors( where: { - actedInConnection_SINGLE: { - node: { actorsConnection_SINGLE: { node: { name_EQ: "Keanu Reeves" } } } + actedInConnection: { + single: { + node: { actorsConnection: { single: { node: { name: { eq: "Keanu Reeves" } } } } } + } } } ) { @@ -167,7 +172,8 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE (single(this1 IN [(this)-[this3:ACTED_IN]->(this1:Movie) WHERE single(this0 IN [(this1)<-[this2:ACTED_IN]-(this0:Actor) WHERE this0.name = $param0 | 1] WHERE true) | 1] WHERE true) XOR single(this5 IN [(this)-[this7:ACTED_IN]->(this5:Series) WHERE single(this4 IN [(this5)<-[this6:STARRED_IN]-(this4:Actor) WHERE this4.name = $param1 | 1] WHERE true) | 1] WHERE true)) CALL { WITH this @@ -201,7 +207,9 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { { actors( where: { - actedInConnection_NONE: { node: { actorsConnection_NONE: { node: { name_EQ: "Keanu Reeves" } } } } + actedInConnection: { + none: { node: { actorsConnection: { none: { node: { name: { eq: "Keanu Reeves" } } } } } } + } } ) { actedIn { @@ -214,7 +222,8 @@ describe("https://github.com/neo4j/graphql/issues/4704", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE (NOT (EXISTS { MATCH (this)-[this0:ACTED_IN]->(this1:Movie) WHERE NOT (EXISTS { diff --git a/packages/graphql/tests/tck/issues/4741.test.ts b/packages/graphql/tests/tck/issues/4741.test.ts index 2bf44fe070..c1cdf6637a 100644 --- a/packages/graphql/tests/tck/issues/4741.test.ts +++ b/packages/graphql/tests/tck/issues/4741.test.ts @@ -45,7 +45,7 @@ describe("https://github.com/neo4j/graphql/issues/4741", () => { test("Filters by relationship aggregation", async () => { const query = /* GraphQL */ ` query { - opportunitiesConnection(first: 10, where: { listsOlisAggregate: { count_GT: 1 } }) { + opportunitiesConnection(first: 10, where: { listsOlisAggregate: { count: { gt: 1 } } }) { edges { node { country @@ -61,7 +61,8 @@ describe("https://github.com/neo4j/graphql/issues/4741", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Opportunity) + "CYPHER 5 + MATCH (this0:Opportunity) CALL { WITH this0 MATCH (this0)-[this1:HAS_LIST]->(this2:ListOli) diff --git a/packages/graphql/tests/tck/issues/4814.test.ts b/packages/graphql/tests/tck/issues/4814.test.ts index c728180342..6ad29c61c7 100644 --- a/packages/graphql/tests/tck/issues/4814.test.ts +++ b/packages/graphql/tests/tck/issues/4814.test.ts @@ -54,7 +54,7 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { test("should use the direction specified in the typeDefs (direction: OUT, connection fields)", async () => { const query = /* GraphQL */ ` query GetNextStep { - steps(where: { id_EQ: "2" }) { + steps(where: { id: { eq: "2" } }) { __typename nextsConnection { edges { @@ -71,7 +71,8 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:AStep) WHERE this0.id = $param0 CALL { @@ -131,7 +132,7 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { test("should use the direction specified in the typeDefs (direction: IN, connection fields)", async () => { const query = /* GraphQL */ ` query GetNextStep { - steps(where: { id_EQ: "2" }) { + steps(where: { id: { eq: "2" } }) { __typename prevsConnection { edges { @@ -148,7 +149,8 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:AStep) WHERE this0.id = $param0 CALL { @@ -208,7 +210,7 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { test("should use the direction specified in the typeDefs (direction: OUT, relationship fields)", async () => { const query = /* GraphQL */ ` query GetNextStep { - steps(where: { id_EQ: "2" }) { + steps(where: { id: { eq: "2" } }) { __typename nexts { id @@ -221,7 +223,8 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:AStep) WHERE this0.id = $param0 CALL { @@ -279,7 +282,7 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { test("should use the direction specified in the typeDefs (direction: IN, relationship fields)", async () => { const query = /* GraphQL */ ` query GetNextStep { - steps(where: { id_EQ: "2" }) { + steps(where: { id: { eq: "2" } }) { __typename prevs { id @@ -292,7 +295,8 @@ describe("https://github.com/neo4j/graphql/issues/4814", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (this0:AStep) WHERE this0.id = $param0 CALL { diff --git a/packages/graphql/tests/tck/issues/4831.test.ts b/packages/graphql/tests/tck/issues/4831.test.ts index e1d17c32d1..507a2283c9 100644 --- a/packages/graphql/tests/tck/issues/4831.test.ts +++ b/packages/graphql/tests/tck/issues/4831.test.ts @@ -51,7 +51,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -84,7 +85,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -117,7 +119,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -146,7 +149,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -177,7 +181,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -210,7 +215,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -243,7 +249,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { @@ -272,7 +279,8 @@ describe("https://github.com/neo4j/graphql/issues/4831", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) + "CYPHER 5 + MATCH (this:Test) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/4838.test.ts b/packages/graphql/tests/tck/issues/4838.test.ts index 89171b2853..cd69e77e44 100644 --- a/packages/graphql/tests/tck/issues/4838.test.ts +++ b/packages/graphql/tests/tck/issues/4838.test.ts @@ -55,7 +55,8 @@ describe("https://github.com/neo4j/graphql/issues/4838", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Test) @@ -99,7 +100,8 @@ describe("https://github.com/neo4j/graphql/issues/4838", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:ParentTest) @@ -116,6 +118,7 @@ describe("https://github.com/neo4j/graphql/issues/4838", () => { CALL { WITH create_this1 MATCH (create_this1)-[create_this6:REL]->(create_this7:Test) + WITH DISTINCT create_this7 CALL { WITH create_this7 CALL { diff --git a/packages/graphql/tests/tck/issues/487.test.ts b/packages/graphql/tests/tck/issues/487.test.ts index d98d07aec2..5fc3033ef9 100644 --- a/packages/graphql/tests/tck/issues/487.test.ts +++ b/packages/graphql/tests/tck/issues/487.test.ts @@ -33,12 +33,12 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { type Book @node { id: ID! - author: Author! @relationship(type: "WROTE", direction: IN) + author: [Author!]! @relationship(type: "WROTE", direction: IN) } type Movie @node { id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) + director: [Director!]! @relationship(type: "DIRECTED", direction: IN) } union Thing = Book | Movie @@ -86,7 +86,8 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (node) WHERE \\"Book\\" IN labels(node) OR @@ -103,8 +104,9 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { CALL { WITH this0 MATCH (this0)<-[this1:WROTE]-(this2:Author) + WITH DISTINCT this2 WITH this2 { .id } AS this2 - RETURN head(collect(this2)) AS var3 + RETURN collect(this2) AS var3 } WITH this0 { .id, author: var3, __resolveType: \\"Book\\", __id: id(this0) } AS this0 RETURN this0 AS var4 @@ -115,8 +117,9 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { CALL { WITH this0 MATCH (this0)<-[this5:DIRECTED]-(this6:Director) + WITH DISTINCT this6 WITH this6 { .id } AS this6 - RETURN head(collect(this6)) AS var7 + RETURN collect(this6) AS var7 } WITH this0 { .id, director: var7, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 RETURN this0 AS var4 @@ -141,12 +144,12 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { type Book implements Thing @node { id: ID! - author: Author! @relationship(type: "WROTE", direction: IN) + author: [Author!]! @relationship(type: "WROTE", direction: IN) } type Movie implements Thing @node { id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) + director: [Director!]! @relationship(type: "DIRECTED", direction: IN) } interface Thing { @@ -195,7 +198,8 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (node) WHERE \\"Book\\" IN labels(node) OR @@ -212,8 +216,9 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { CALL { WITH this0 MATCH (this0)<-[this1:WROTE]-(this2:Author) + WITH DISTINCT this2 WITH this2 { .id } AS this2 - RETURN head(collect(this2)) AS var3 + RETURN collect(this2) AS var3 } WITH this0 { .id, author: var3, __resolveType: \\"Book\\", __id: id(this0) } AS this0 RETURN this0 AS var4 @@ -224,8 +229,9 @@ describe("https://github.com/neo4j/graphql/issues/487", () => { CALL { WITH this0 MATCH (this0)<-[this5:DIRECTED]-(this6:Director) + WITH DISTINCT this6 WITH this6 { .id } AS this6 - RETURN head(collect(this6)) AS var7 + RETURN collect(this6) AS var7 } WITH this0 { .id, director: var7, __resolveType: \\"Movie\\", __id: id(this0) } AS this0 RETURN this0 AS var4 diff --git a/packages/graphql/tests/tck/issues/488.test.ts b/packages/graphql/tests/tck/issues/488.test.ts index dc87541b8f..411e51e203 100644 --- a/packages/graphql/tests/tck/issues/488.test.ts +++ b/packages/graphql/tests/tck/issues/488.test.ts @@ -34,17 +34,17 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { union Keyword = Emoji | Hashtag | Text type Emoji @node { - id: ID! @id @unique + id: ID! @id type: String! } type Hashtag @node { - id: ID! @id @unique + id: ID! @id type: String! } type Text @node { - id: ID! @id @unique + id: ID! @id type: String! } `; @@ -57,7 +57,7 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { test("Should replicate issue and return correct cypher", async () => { const query = /* GraphQL */ ` query { - journalists(where: { keywordsConnection_SOME: { Emoji: { node: { type_EQ: "Smile" } } } }) { + journalists(where: { keywordsConnection: { some: { Emoji: { node: { type: { eq: "Smile" } } } } } }) { name keywords { ... on Emoji { @@ -72,7 +72,8 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Journalist) + "CYPHER 5 + MATCH (this:Journalist) WHERE EXISTS { MATCH (this)-[this0:HAS_KEYWORD]->(this1:Emoji) WHERE this1.type = $param0 @@ -111,7 +112,7 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { test("Should replicate issue and return correct cypher (using NONE)", async () => { const query = /* GraphQL */ ` query { - journalists(where: { keywordsConnection_NONE: { Emoji: { node: { type_EQ: "Smile" } } } }) { + journalists(where: { keywordsConnection: { none: { Emoji: { node: { type: { eq: "Smile" } } } } } }) { name keywords { ... on Emoji { @@ -126,7 +127,8 @@ describe("https://github.com/neo4j/graphql/issues/488", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Journalist) + "CYPHER 5 + MATCH (this:Journalist) WHERE NOT (EXISTS { MATCH (this)-[this0:HAS_KEYWORD]->(this1:Emoji) WHERE this1.type = $param0 diff --git a/packages/graphql/tests/tck/issues/5023.test.ts b/packages/graphql/tests/tck/issues/5023.test.ts index c55226e816..2d481cd78e 100644 --- a/packages/graphql/tests/tck/issues/5023.test.ts +++ b/packages/graphql/tests/tck/issues/5023.test.ts @@ -30,23 +30,27 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { type JWT @jwt { id: String } - type User @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + type User @authorization(filter: [{ where: { node: { userId: { eq: "$jwt.id" } } } }]) @node { + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } type Tenant - @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) + @authorization(validate: [{ where: { node: { admins: { some: { userId: { eq: "$jwt.id" } } } } } }]) @node { id: ID! @id admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN, aggregate: false) - settings: Settings! @relationship(type: "HAS_SETTINGS", direction: OUT, aggregate: false) + settings: [Settings!]! @relationship(type: "HAS_SETTINGS", direction: OUT, aggregate: false) } type Settings @node - @authorization(validate: [{ where: { node: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { - tenant: Tenant! @relationship(type: "HAS_SETTINGS", direction: IN, aggregate: false) + @authorization( + validate: [ + { where: { node: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } } } + ] + ) { + tenant: [Tenant!]! @relationship(type: "HAS_SETTINGS", direction: IN, aggregate: false) extendedOpeningHours: [OpeningDay!]! @relationship(type: "HAS_OPENING_HOURS", direction: OUT, aggregate: false) } @@ -54,9 +58,19 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { type OpeningDay @node @authorization( - validate: [{ where: { node: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } } }] + validate: [ + { + where: { + node: { + settings: { + some: { tenant: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } + } + } + } + } + ] ) { - settings: Settings! @relationship(type: "HAS_OPENING_HOURS", direction: IN, aggregate: false) + settings: [Settings!]! @relationship(type: "HAS_OPENING_HOURS", direction: IN, aggregate: false) date: Date open: [OpeningHoursInterval!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: OUT, aggregate: false) @@ -69,13 +83,23 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { { where: { node: { - openingDay: { settings: { tenant: { admins_SOME: { userId_EQ: "$jwt.id" } } } } + openingDay: { + some: { + settings: { + some: { + tenant: { + some: { admins: { some: { userId: { eq: "$jwt.id" } } } } + } + } + } + } + } } } } ] ) { - openingDay: OpeningDay @relationship(type: "HAS_OPEN_INTERVALS", direction: IN, aggregate: false) + openingDay: [OpeningDay!]! @relationship(type: "HAS_OPEN_INTERVALS", direction: IN, aggregate: false) name: String } @@ -115,31 +139,38 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Tenant) + "CYPHER 5 + MATCH (this:Tenant) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:ADMIN_IN]-(this0:User) WHERE ($jwt.id IS NOT NULL AND this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:ADMIN_IN]-(this0:User) + WHERE ($jwt.id IS NOT NULL AND this0.userId = $jwt.id) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this CALL { WITH this MATCH (this)-[this_has_settings0_relationship:HAS_SETTINGS]->(this_settings0:Settings) - OPTIONAL MATCH (this_settings0)<-[:HAS_SETTINGS]-(authorization_updatebefore_this2:Tenant) - WITH *, count(authorization_updatebefore_this2) AS authorization_updatebefore_var0 - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization_updatebefore_var0 <> 0 AND size([(authorization_updatebefore_this2)<-[:ADMIN_IN]-(authorization_updatebefore_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization_updatebefore_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_settings0)<-[:HAS_SETTINGS]-(authorization_updatebefore_this0:Tenant) + WHERE EXISTS { + MATCH (authorization_updatebefore_this0)<-[:ADMIN_IN]-(authorization_updatebefore_this1:User) + WHERE ($jwt.id IS NOT NULL AND authorization_updatebefore_this1.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * CALL { WITH * OPTIONAL MATCH (this_settings0)-[this_settings0_extendedOpeningHours0_delete0_relationship:HAS_OPENING_HOURS]->(this_settings0_extendedOpeningHours0_delete0:OpeningDay) - CALL { - WITH this_settings0_extendedOpeningHours0_delete0 - MATCH (this_settings0_extendedOpeningHours0_delete0)<-[:HAS_OPENING_HOURS]-(authorization_deletebefore_this1:Settings) - OPTIONAL MATCH (authorization_deletebefore_this1)<-[:HAS_SETTINGS]-(authorization_deletebefore_this2:Tenant) - WITH *, count(authorization_deletebefore_this2) AS authorization_deletebefore_var3 - WITH * - WHERE (authorization_deletebefore_var3 <> 0 AND size([(authorization_deletebefore_this2)<-[:ADMIN_IN]-(authorization_deletebefore_this4:User) WHERE ($jwt.id IS NOT NULL AND authorization_deletebefore_this4.userId = $jwt.id) | 1]) > 0) - RETURN count(authorization_deletebefore_this1) = 1 AS authorization_deletebefore_var0 - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND authorization_deletebefore_var0 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_settings0_extendedOpeningHours0_delete0)<-[:HAS_OPENING_HOURS]-(authorization_deletebefore_this0:Settings) + WHERE EXISTS { + MATCH (authorization_deletebefore_this0)<-[:HAS_SETTINGS]-(authorization_deletebefore_this1:Tenant) + WHERE EXISTS { + MATCH (authorization_deletebefore_this1)<-[:ADMIN_IN]-(authorization_deletebefore_this2:User) + WHERE ($jwt.id IS NOT NULL AND authorization_deletebefore_this2.userId = $jwt.id) + } + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this_settings0_extendedOpeningHours0_delete0_relationship, collect(DISTINCT this_settings0_extendedOpeningHours0_delete0) AS this_settings0_extendedOpeningHours0_delete0_to_delete CALL { WITH this_settings0_extendedOpeningHours0_delete0_to_delete @@ -148,84 +179,80 @@ describe("https://github.com/neo4j/graphql/issues/5023", () => { } } WITH this, this_settings0 - OPTIONAL MATCH (this_settings0)<-[:HAS_SETTINGS]-(authorization__after_this2:Tenant) - WITH *, count(authorization__after_this2) AS authorization__after_var0 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (authorization__after_var0 <> 0 AND size([(authorization__after_this2)<-[:ADMIN_IN]-(authorization__after_this1:User) WHERE ($jwt.id IS NOT NULL AND authorization__after_this1.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH this, this_settings0 - CALL { - WITH this_settings0 - MATCH (this_settings0)<-[this_settings0_tenant_Tenant_unique:HAS_SETTINGS]-(:Tenant) - WITH count(this_settings0_tenant_Tenant_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDSettings.tenant required exactly once', [0]) - RETURN c AS this_settings0_tenant_Tenant_unique_ignored - } + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this_settings0)<-[:HAS_SETTINGS]-(authorization__after_this0:Tenant) + WHERE EXISTS { + MATCH (authorization__after_this0)<-[:ADMIN_IN]-(authorization__after_this1:User) + WHERE ($jwt.id IS NOT NULL AND authorization__after_this1.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) RETURN count(*) AS update_this_settings0 } WITH this - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:ADMIN_IN]-(authorization__after_this0:User) WHERE ($jwt.id IS NOT NULL AND authorization__after_this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH * - CALL { - WITH this - MATCH (this)-[this_settings_Settings_unique:HAS_SETTINGS]->(:Settings) - WITH count(this_settings_Settings_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDTenant.settings required exactly once', [0]) - RETURN c AS this_settings_Settings_unique_ignored - } + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:ADMIN_IN]-(authorization__after_this0:User) + WHERE ($jwt.id IS NOT NULL AND authorization__after_this0.userId = $jwt.id) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND size([(this)<-[:ADMIN_IN]-(update_this0:User) WHERE ($jwt.id IS NOT NULL AND update_this0.userId = $jwt.id) | 1]) > 0), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this)<-[:ADMIN_IN]-(update_this0:User) + WHERE ($jwt.id IS NOT NULL AND update_this0.userId = $jwt.id) + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH this MATCH (this)-[update_this1:HAS_SETTINGS]->(update_this2:Settings) - OPTIONAL MATCH (update_this2)<-[:HAS_SETTINGS]-(update_this3:Tenant) - WITH *, count(update_this3) AS update_var4 + WITH DISTINCT update_this2 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (update_var4 <> 0 AND size([(update_this3)<-[:ADMIN_IN]-(update_this5:User) WHERE ($jwt.id IS NOT NULL AND update_this5.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (update_this2)<-[:HAS_SETTINGS]-(update_this3:Tenant) + WHERE EXISTS { + MATCH (update_this3)<-[:ADMIN_IN]-(update_this4:User) + WHERE ($jwt.id IS NOT NULL AND update_this4.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) CALL { WITH update_this2 - MATCH (update_this2)-[update_this6:HAS_OPENING_HOURS]->(update_this7:OpeningDay) - CALL { - WITH update_this7 - MATCH (update_this7)<-[:HAS_OPENING_HOURS]-(update_this8:Settings) - OPTIONAL MATCH (update_this8)<-[:HAS_SETTINGS]-(update_this9:Tenant) - WITH *, count(update_this9) AS update_var10 - WITH * - WHERE (update_var10 <> 0 AND size([(update_this9)<-[:ADMIN_IN]-(update_this11:User) WHERE ($jwt.id IS NOT NULL AND update_this11.userId = $jwt.id) | 1]) > 0) - RETURN count(update_this8) = 1 AS update_var12 - } + MATCH (update_this2)-[update_this5:HAS_OPENING_HOURS]->(update_this6:OpeningDay) + WITH DISTINCT update_this6 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND update_var12 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH update_this7 - MATCH (update_this7)-[update_this13:HAS_OPEN_INTERVALS]->(update_this14:OpeningHoursInterval) - CALL { - WITH update_this14 - MATCH (update_this14)<-[:HAS_OPEN_INTERVALS]-(update_this15:OpeningDay) - CALL { - WITH update_this15 - MATCH (update_this15)<-[:HAS_OPENING_HOURS]-(update_this16:Settings) - OPTIONAL MATCH (update_this16)<-[:HAS_SETTINGS]-(update_this17:Tenant) - WITH *, count(update_this17) AS update_var18 - WITH * - WHERE (update_var18 <> 0 AND size([(update_this17)<-[:ADMIN_IN]-(update_this19:User) WHERE ($jwt.id IS NOT NULL AND update_this19.userId = $jwt.id) | 1]) > 0) - RETURN count(update_this16) = 1 AS update_var20 + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (update_this6)<-[:HAS_OPENING_HOURS]-(update_this7:Settings) + WHERE EXISTS { + MATCH (update_this7)<-[:HAS_SETTINGS]-(update_this8:Tenant) + WHERE EXISTS { + MATCH (update_this8)<-[:ADMIN_IN]-(update_this9:User) + WHERE ($jwt.id IS NOT NULL AND update_this9.userId = $jwt.id) } - WITH * - WHERE update_var20 = true - RETURN count(update_this15) = 1 AS update_var21 } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + CALL { + WITH update_this6 + MATCH (update_this6)-[update_this10:HAS_OPEN_INTERVALS]->(update_this11:OpeningHoursInterval) + WITH DISTINCT update_this11 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND update_var21 = true), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH update_this14 { .name } AS update_this14 - RETURN collect(update_this14) AS update_var22 + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (update_this11)<-[:HAS_OPEN_INTERVALS]-(update_this12:OpeningDay) + WHERE EXISTS { + MATCH (update_this12)<-[:HAS_OPENING_HOURS]-(update_this13:Settings) + WHERE EXISTS { + MATCH (update_this13)<-[:HAS_SETTINGS]-(update_this14:Tenant) + WHERE EXISTS { + MATCH (update_this14)<-[:ADMIN_IN]-(update_this15:User) + WHERE ($jwt.id IS NOT NULL AND update_this15.userId = $jwt.id) + } + } + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH update_this11 { .name } AS update_this11 + RETURN collect(update_this11) AS update_var16 } - WITH update_this7 { open: update_var22 } AS update_this7 - RETURN collect(update_this7) AS update_var23 + WITH update_this6 { open: update_var16 } AS update_this6 + RETURN collect(update_this6) AS update_var17 } - WITH update_this2 { extendedOpeningHours: update_var23 } AS update_this2 - RETURN head(collect(update_this2)) AS update_var24 + WITH update_this2 { extendedOpeningHours: update_var17 } AS update_this2 + RETURN collect(update_this2) AS update_var18 } - RETURN collect(DISTINCT this { settings: update_var24 }) AS data" + RETURN collect(DISTINCT this { settings: update_var18 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/issues/5030.test.ts b/packages/graphql/tests/tck/issues/5030.test.ts index 264a95b516..503dea492d 100644 --- a/packages/graphql/tests/tck/issues/5030.test.ts +++ b/packages/graphql/tests/tck/issues/5030.test.ts @@ -26,7 +26,9 @@ describe("https://github.com/neo4j/graphql/issues/5030", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` - type Movie @fulltext(indexes: [{ name: "MovieTitle", fields: ["title"] }]) @node { + type Movie + @fulltext(indexes: [{ indexName: "MovieTitle", queryName: "moviesByTitle", fields: ["title"] }]) + @node { title: String released: Int } @@ -60,7 +62,8 @@ describe("https://github.com/neo4j/graphql/issues/5030", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (m:Movie) RETURN m as this } WITH this AS this0 diff --git a/packages/graphql/tests/tck/issues/505.test.ts b/packages/graphql/tests/tck/issues/505.test.ts index 4521da56da..60bdebb22e 100644 --- a/packages/graphql/tests/tck/issues/505.test.ts +++ b/packages/graphql/tests/tck/issues/505.test.ts @@ -42,8 +42,8 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { operations: [READ] where: { OR: [ - { node: { members_SOME: { authId_EQ: "$jwt.sub" } } } - { node: { admins_SOME: { authId_EQ: "$jwt.sub" } } } + { node: { members: { some: { authId: { eq: "$jwt.sub" } } } } } + { node: { admins: { some: { authId: { eq: "$jwt.sub" } } } } } ] } } @@ -66,16 +66,18 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { where: { node: { OR: [ - { owner: { authId_EQ: "$jwt.sub" } } + { owner: { some: { authId: { eq: "$jwt.sub" } } } } { AND: [ - { shared_EQ: true } + { shared: { eq: true } } { workspace: { - OR: [ - { members_SOME: { authId_EQ: "$jwt.sub" } } - { admins_SOME: { authId_EQ: "$jwt.sub" } } - ] + all: { + OR: [ + { members: { some: { authId: { eq: "$jwt.sub" } } } } + { admins: { some: { authId: { eq: "$jwt.sub" } } } } + ] + } } } ] @@ -90,9 +92,9 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { shared: Boolean! @default(value: false) - owner: User! @relationship(type: "CREATED_PAGE", direction: IN) + owner: [User!]! @relationship(type: "CREATED_PAGE", direction: IN) - workspace: Workspace! @relationship(type: "HAS_PAGE", direction: IN) + workspace: [Workspace!]! @relationship(type: "HAS_PAGE", direction: IN) } `; @@ -105,7 +107,7 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { test("Users query", async () => { const query = /* GraphQL */ ` query Users { - users(where: { id_EQ: "my-user-id" }) { + users(where: { id: { eq: "my-user-id" } }) { id authId createdPages { @@ -118,21 +120,40 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.id = $param0 CALL { WITH this MATCH (this)-[this0:CREATED_PAGE]->(this1:Page) - OPTIONAL MATCH (this1)<-[:CREATED_PAGE]-(this2:User) - WITH *, count(this2) AS var3 - OPTIONAL MATCH (this1)<-[:HAS_PAGE]-(this4:Workspace) - WITH *, count(this4) AS var5 + WITH DISTINCT this1 WITH * - WHERE ($isAuthenticated = true AND ((var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.authId = $jwt.sub)) OR (($param3 IS NOT NULL AND this1.shared = $param3) AND (var5 <> 0 AND (size([(this4)<-[:MEMBER_OF]-(this6:User) WHERE ($jwt.sub IS NOT NULL AND this6.authId = $jwt.sub) | 1]) > 0 OR size([(this4)-[:HAS_ADMIN]->(this7:User) WHERE ($jwt.sub IS NOT NULL AND this7.authId = $jwt.sub) | 1]) > 0))))) + WHERE ($isAuthenticated = true AND (EXISTS { + MATCH (this1)<-[:CREATED_PAGE]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.authId = $jwt.sub) + } OR (($param3 IS NOT NULL AND this1.shared = $param3) AND (EXISTS { + MATCH (this1)<-[:HAS_PAGE]-(this3:Workspace) + WHERE (EXISTS { + MATCH (this3)<-[:MEMBER_OF]-(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) + } OR EXISTS { + MATCH (this3)-[:HAS_ADMIN]->(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.authId = $jwt.sub) + }) + } AND NOT (EXISTS { + MATCH (this1)<-[:HAS_PAGE]-(this3:Workspace) + WHERE NOT (EXISTS { + MATCH (this3)<-[:MEMBER_OF]-(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) + } OR EXISTS { + MATCH (this3)-[:HAS_ADMIN]->(this5:User) + WHERE ($jwt.sub IS NOT NULL AND this5.authId = $jwt.sub) + }) + }))))) WITH this1 { .id } AS this1 - RETURN collect(this1) AS var8 + RETURN collect(this1) AS var6 } - RETURN this { .id, .authId, createdPages: var8 } AS this" + RETURN this { .id, .authId, createdPages: var6 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -148,7 +169,7 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { test("Workspaces query", async () => { const query = /* GraphQL */ ` query Workspaces { - workspaces(where: { id_EQ: "my-workspace-id" }) { + workspaces(where: { id: { eq: "my-workspace-id" } }) { id pages { id @@ -160,22 +181,47 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Workspace) + "CYPHER 5 + MATCH (this:Workspace) WITH * - WHERE (this.id = $param0 AND ($isAuthenticated = true AND (size([(this)<-[:MEMBER_OF]-(this0:User) WHERE ($jwt.sub IS NOT NULL AND this0.authId = $jwt.sub) | 1]) > 0 OR size([(this)-[:HAS_ADMIN]->(this1:User) WHERE ($jwt.sub IS NOT NULL AND this1.authId = $jwt.sub) | 1]) > 0))) + WHERE (this.id = $param0 AND ($isAuthenticated = true AND (EXISTS { + MATCH (this)<-[:MEMBER_OF]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.authId = $jwt.sub) + } OR EXISTS { + MATCH (this)-[:HAS_ADMIN]->(this1:User) + WHERE ($jwt.sub IS NOT NULL AND this1.authId = $jwt.sub) + }))) CALL { WITH this MATCH (this)-[this2:HAS_PAGE]->(this3:Page) - OPTIONAL MATCH (this3)<-[:CREATED_PAGE]-(this4:User) - WITH *, count(this4) AS var5 - OPTIONAL MATCH (this3)<-[:HAS_PAGE]-(this6:Workspace) - WITH *, count(this6) AS var7 + WITH DISTINCT this3 WITH * - WHERE ($isAuthenticated = true AND ((var5 <> 0 AND ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub)) OR (($param3 IS NOT NULL AND this3.shared = $param3) AND (var7 <> 0 AND (size([(this6)<-[:MEMBER_OF]-(this8:User) WHERE ($jwt.sub IS NOT NULL AND this8.authId = $jwt.sub) | 1]) > 0 OR size([(this6)-[:HAS_ADMIN]->(this9:User) WHERE ($jwt.sub IS NOT NULL AND this9.authId = $jwt.sub) | 1]) > 0))))) + WHERE ($isAuthenticated = true AND (EXISTS { + MATCH (this3)<-[:CREATED_PAGE]-(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) + } OR (($param3 IS NOT NULL AND this3.shared = $param3) AND (EXISTS { + MATCH (this3)<-[:HAS_PAGE]-(this5:Workspace) + WHERE (EXISTS { + MATCH (this5)<-[:MEMBER_OF]-(this6:User) + WHERE ($jwt.sub IS NOT NULL AND this6.authId = $jwt.sub) + } OR EXISTS { + MATCH (this5)-[:HAS_ADMIN]->(this7:User) + WHERE ($jwt.sub IS NOT NULL AND this7.authId = $jwt.sub) + }) + } AND NOT (EXISTS { + MATCH (this3)<-[:HAS_PAGE]-(this5:Workspace) + WHERE NOT (EXISTS { + MATCH (this5)<-[:MEMBER_OF]-(this6:User) + WHERE ($jwt.sub IS NOT NULL AND this6.authId = $jwt.sub) + } OR EXISTS { + MATCH (this5)-[:HAS_ADMIN]->(this7:User) + WHERE ($jwt.sub IS NOT NULL AND this7.authId = $jwt.sub) + }) + }))))) WITH this3 { .id } AS this3 - RETURN collect(this3) AS var10 + RETURN collect(this3) AS var8 } - RETURN this { .id, pages: var10 } AS this" + RETURN this { .id, pages: var8 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -191,7 +237,7 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { test("Pages query", async () => { const query = /* GraphQL */ ` query Pages { - pages(where: { workspace: { id_EQ: "my-workspace-id" } }) { + pages(where: { workspace: { all: { id: { eq: "my-workspace-id" } } } }) { id } } @@ -200,15 +246,37 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Page) - OPTIONAL MATCH (this)<-[:HAS_PAGE]-(this0:Workspace) - WITH *, count(this0) AS var1 - OPTIONAL MATCH (this)<-[:CREATED_PAGE]-(this2:User) - WITH *, count(this2) AS var3 - OPTIONAL MATCH (this)<-[:HAS_PAGE]-(this4:Workspace) - WITH *, count(this4) AS var5 + "CYPHER 5 + MATCH (this:Page) WITH * - WHERE ((var1 <> 0 AND this0.id = $param0) AND ($isAuthenticated = true AND ((var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.authId = $jwt.sub)) OR (($param3 IS NOT NULL AND this.shared = $param3) AND (var5 <> 0 AND (size([(this4)<-[:MEMBER_OF]-(this6:User) WHERE ($jwt.sub IS NOT NULL AND this6.authId = $jwt.sub) | 1]) > 0 OR size([(this4)-[:HAS_ADMIN]->(this7:User) WHERE ($jwt.sub IS NOT NULL AND this7.authId = $jwt.sub) | 1]) > 0)))))) + WHERE ((EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this0:Workspace) + WHERE this0.id = $param0 + } AND NOT (EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this0:Workspace) + WHERE NOT (this0.id = $param0) + })) AND ($isAuthenticated = true AND (EXISTS { + MATCH (this)<-[:CREATED_PAGE]-(this1:User) + WHERE ($jwt.sub IS NOT NULL AND this1.authId = $jwt.sub) + } OR (($param3 IS NOT NULL AND this.shared = $param3) AND (EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this2:Workspace) + WHERE (EXISTS { + MATCH (this2)<-[:MEMBER_OF]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.authId = $jwt.sub) + } OR EXISTS { + MATCH (this2)-[:HAS_ADMIN]->(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) + }) + } AND NOT (EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this2:Workspace) + WHERE NOT (EXISTS { + MATCH (this2)<-[:MEMBER_OF]-(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.authId = $jwt.sub) + } OR EXISTS { + MATCH (this2)-[:HAS_ADMIN]->(this4:User) + WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) + }) + })))))) RETURN this { .id } AS this" `); @@ -234,13 +302,31 @@ describe("https://github.com/neo4j/graphql/issues/505", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Page) - OPTIONAL MATCH (this)<-[:CREATED_PAGE]-(this0:User) - WITH *, count(this0) AS var1 - OPTIONAL MATCH (this)<-[:HAS_PAGE]-(this2:Workspace) - WITH *, count(this2) AS var3 + "CYPHER 5 + MATCH (this:Page) WITH * - WHERE ($isAuthenticated = true AND ((var1 <> 0 AND ($jwt.sub IS NOT NULL AND this0.authId = $jwt.sub)) OR (($param2 IS NOT NULL AND this.shared = $param2) AND (var3 <> 0 AND (size([(this2)<-[:MEMBER_OF]-(this4:User) WHERE ($jwt.sub IS NOT NULL AND this4.authId = $jwt.sub) | 1]) > 0 OR size([(this2)-[:HAS_ADMIN]->(this5:User) WHERE ($jwt.sub IS NOT NULL AND this5.authId = $jwt.sub) | 1]) > 0))))) + WHERE ($isAuthenticated = true AND (EXISTS { + MATCH (this)<-[:CREATED_PAGE]-(this0:User) + WHERE ($jwt.sub IS NOT NULL AND this0.authId = $jwt.sub) + } OR (($param2 IS NOT NULL AND this.shared = $param2) AND (EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this1:Workspace) + WHERE (EXISTS { + MATCH (this1)<-[:MEMBER_OF]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.authId = $jwt.sub) + } OR EXISTS { + MATCH (this1)-[:HAS_ADMIN]->(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.authId = $jwt.sub) + }) + } AND NOT (EXISTS { + MATCH (this)<-[:HAS_PAGE]-(this1:Workspace) + WHERE NOT (EXISTS { + MATCH (this1)<-[:MEMBER_OF]-(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.authId = $jwt.sub) + } OR EXISTS { + MATCH (this1)-[:HAS_ADMIN]->(this3:User) + WHERE ($jwt.sub IS NOT NULL AND this3.authId = $jwt.sub) + }) + }))))) RETURN this { .id } AS this" `); diff --git a/packages/graphql/tests/tck/issues/5066.test.ts b/packages/graphql/tests/tck/issues/5066.test.ts deleted file mode 100644 index b29ba3579f..0000000000 --- a/packages/graphql/tests/tck/issues/5066.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/5066", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - const secret = "secret"; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type AdminGroup - @node(labels: ["AdminGroup"]) - @mutation(operations: []) - @authorization(filter: [{ where: { node: { createdBy: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - createdBy: User! - @relationship(type: "CREATED_ADMIN_GROUP", direction: IN) - @settable(onCreate: true, onUpdate: false) - } - - type User - @node(labels: ["User"]) - @mutation(operations: []) - @authorization( - filter: [{ where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }] - ) { - id: ID! @unique @settable(onCreate: true, onUpdate: false) - createdAt: DateTime! @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - username: String! @unique - blockedUsers: [UserBlockedUser!]! @relationship(type: "HAS_BLOCKED", direction: OUT) - createdAdminGroups: [AdminGroup!]! @relationship(type: "CREATED_ADMIN_GROUP", direction: OUT) - } - - type UserBlockedUser - @node(labels: ["UserBlockedUser"]) - @query(read: false, aggregate: false) - @mutation(operations: []) - @authorization(filter: [{ where: { node: { from: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - from: User! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) - to: User! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) - } - - union PartyCreator = User | AdminGroup - - type Party - @node(labels: ["Party"]) - @mutation(operations: []) - @authorization( - filter: [ - { where: { node: { createdByConnection: { User: { node: { id_EQ: "$jwt.sub" } } } } } } - { - where: { - node: { - createdByConnection: { AdminGroup: { node: { createdBy: { id_EQ: "$jwt.sub" } } } } - } - } - } - ] - ) { - id: ID! @id @unique - createdAt: DateTime! @timestamp(operations: [CREATE]) @private - updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @private - createdBy: PartyCreator! - @relationship(type: "CREATED_PARTY", direction: IN) - @settable(onCreate: true, onUpdate: false) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { - key: secret, - }, - }, - }); - }); - - test("filter unions with authotization", async () => { - const query = /* GraphQL */ ` - query Parties { - parties { - id - createdBy { - ... on User { - username - } - } - } - } - `; - - const token = createBearerToken(secret, { sub: "1" }); - const result = await translateQuery(neoSchema, query, { - contextValues: { - token, - }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Party) - CALL { - WITH this - MATCH (this)<-[this0:CREATED_PARTY]-(this1:AdminGroup) - OPTIONAL MATCH (this1)<-[:CREATED_ADMIN_GROUP]-(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)) - RETURN count(this1) > 0 AS var4 - } - WITH * - WHERE (($isAuthenticated = true AND single(this5 IN [(this)<-[this6:CREATED_PARTY]-(this5:User) WHERE ($jwt.sub IS NOT NULL AND this5.id = $jwt.sub) | 1] WHERE true)) OR ($isAuthenticated = true AND var4 = true)) - CALL { - WITH this - CALL { - WITH * - MATCH (this)<-[this7:CREATED_PARTY]-(this8:User) - CALL { - WITH this8 - MATCH (this8)-[:HAS_BLOCKED]->(this9:UserBlockedUser) - OPTIONAL MATCH (this9)-[:IS_BLOCKING]->(this10:User) - WITH *, count(this10) AS var11 - WITH * - WHERE (var11 <> 0 AND ($jwt.sub IS NOT NULL AND this10.id = $jwt.sub)) - RETURN count(this9) > 0 AS var12 - } - WITH * - WHERE ($isAuthenticated = true AND NOT (var12 = true)) - WITH this8 { .username, __resolveType: \\"User\\", __id: id(this8) } AS this8 - RETURN this8 AS var13 - UNION - WITH * - MATCH (this)<-[this14:CREATED_PARTY]-(this15:AdminGroup) - OPTIONAL MATCH (this15)<-[:CREATED_ADMIN_GROUP]-(this16:User) - WITH *, count(this16) AS var17 - WITH * - WHERE ($isAuthenticated = true AND (var17 <> 0 AND ($jwt.sub IS NOT NULL AND this16.id = $jwt.sub))) - WITH this15 { __resolveType: \\"AdminGroup\\", __id: id(this15) } AS this15 - RETURN this15 AS var13 - } - WITH var13 - RETURN head(collect(var13)) AS var13 - } - RETURN this { .id, createdBy: var13 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"jwt\\": { - \\"roles\\": [], - \\"sub\\": \\"1\\" - }, - \\"isAuthenticated\\": true - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/5080.test.ts b/packages/graphql/tests/tck/issues/5080.test.ts index 5279e7b0b9..b8f946db44 100644 --- a/packages/graphql/tests/tck/issues/5080.test.ts +++ b/packages/graphql/tests/tck/issues/5080.test.ts @@ -31,13 +31,13 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { type JWT @jwt { id: String } - type User @authorization(filter: [{ where: { node: { userId_EQ: "$jwt.id" } } }]) @node { - userId: String! @unique + type User @authorization(filter: [{ where: { node: { userId: { eq: "$jwt.id" } } } }]) @node { + userId: String! adminAccess: [Tenant!]! @relationship(type: "ADMIN_IN", direction: OUT, aggregate: false) } type Tenant - @authorization(validate: [{ where: { node: { admins_SOME: { userId_EQ: "$jwt.id" } } } }]) + @authorization(validate: [{ where: { node: { admins: { some: { userId: { eq: "$jwt.id" } } } } } }]) @node { id: ID! @id admins: [User!]! @relationship(type: "ADMIN_IN", direction: IN, aggregate: false) @@ -67,9 +67,13 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { type Car @node @mutation(operations: [UPDATE]) - @authorization(validate: [{ where: { node: { owner: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { + @authorization( + validate: [ + { where: { node: { owner: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } } } + ] + ) { id: ID! @id - owner: Tenant! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) + owner: [Tenant!]! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) name: String! createdAt: DateTime! @timestamp(operations: [CREATE]) updatedAt: DateTime! @timestamp(operations: [CREATE, UPDATE]) @@ -78,9 +82,13 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { type DeletedCar @node @mutation(operations: [UPDATE]) - @authorization(validate: [{ where: { node: { owner: { admins_SOME: { userId_EQ: "$jwt.id" } } } } }]) { + @authorization( + validate: [ + { where: { node: { owner: { some: { admins: { some: { userId: { eq: "$jwt.id" } } } } } } } } + ] + ) { id: ID! @id - owner: Tenant! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) + owner: [Tenant!]! @relationship(type: "OWNED_BY", direction: OUT, aggregate: false) name: String! reason: String! createdAt: DateTime! @timestamp(operations: [CREATE]) @@ -117,7 +125,8 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH(s:Car) WHERE (s.id = $param0.carId) REMOVE s:Car @@ -126,10 +135,14 @@ describe("https://github.com/neo4j/graphql/issues/5080", () => { RETURN s AS s } WITH s AS this0 - OPTIONAL MATCH (this0)-[:OWNED_BY]->(this1:Tenant) - WITH *, count(this1) AS var2 WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (var2 <> 0 AND size([(this1)<-[:ADMIN_IN]-(this3:User) WHERE ($jwt.id IS NOT NULL AND this3.userId = $jwt.id) | 1]) > 0)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND EXISTS { + MATCH (this0)-[:OWNED_BY]->(this1:Tenant) + WHERE EXISTS { + MATCH (this1)<-[:ADMIN_IN]-(this2:User) + WHERE ($jwt.id IS NOT NULL AND this2.userId = $jwt.id) + } + }), \\"@neo4j/graphql/FORBIDDEN\\", [0]) WITH this0 { .id } AS this0 RETURN this0 AS this" `); diff --git a/packages/graphql/tests/tck/issues/5143.test.ts b/packages/graphql/tests/tck/issues/5143.test.ts index 3d831cdcca..ba03514249 100644 --- a/packages/graphql/tests/tck/issues/5143.test.ts +++ b/packages/graphql/tests/tck/issues/5143.test.ts @@ -34,9 +34,10 @@ describe("https://github.com/neo4j/graphql/issues/5143", () => { type Video @node { id: ID! @id - publisher: User! @relationship(type: "PUBLISHER", direction: IN) + publisher: [User!]! @relationship(type: "PUBLISHER", direction: IN) } - extend type Video @authorization(filter: [{ where: { node: { publisher: { id_EQ: "$jwt.sub" } } } }]) + extend type Video + @authorization(filter: [{ where: { node: { publisher: { all: { id: { eq: "$jwt.sub" } } } } } }]) type Query { getAllVids: [Video]! @@ -78,16 +79,21 @@ describe("https://github.com/neo4j/graphql/issues/5143", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MATCH (video:Video) RETURN video LIMIT 1 } WITH video AS this0 - OPTIONAL MATCH (this0)<-[:PUBLISHER]-(this1:User) - WITH *, count(this1) AS var2 WITH * - WHERE ($isAuthenticated = true AND (var2 <> 0 AND ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub))) + WHERE ($isAuthenticated = true AND (EXISTS { + MATCH (this0)<-[:PUBLISHER]-(this1:User) + WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + } AND NOT (EXISTS { + MATCH (this0)<-[:PUBLISHER]-(this1:User) + WHERE NOT ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + }))) WITH this0 { .id } AS this0 RETURN this0 AS this" `); diff --git a/packages/graphql/tests/tck/issues/5270.test.ts b/packages/graphql/tests/tck/issues/5270.test.ts index ffc122df6e..a2f6cd436b 100644 --- a/packages/graphql/tests/tck/issues/5270.test.ts +++ b/packages/graphql/tests/tck/issues/5270.test.ts @@ -31,18 +31,28 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { type User @node(labels: ["User"]) @authorization( - filter: [{ where: { node: { NOT: { blockedUsers_SOME: { to: { id_EQ: "$jwt.sub" } } } } } }] + filter: [ + { + where: { + node: { NOT: { blockedUsers: { some: { to: { some: { id: { eq: "$jwt.sub" } } } } } } } + } + } + ] ) { - id: ID! @unique @id + id: ID! @id blockedUsers: [UserBlockedUser!]! @relationship(type: "HAS_BLOCKED", direction: OUT) } type UserBlockedUser @node(labels: ["UserBlockedUser"]) - @authorization(filter: [{ where: { node: { from: { id_EQ: "$jwt.sub" } } } }]) { - id: ID! @id @unique - from: User! @relationship(type: "HAS_BLOCKED", direction: IN) @settable(onCreate: true, onUpdate: false) - to: User! @relationship(type: "IS_BLOCKING", direction: OUT) @settable(onCreate: true, onUpdate: false) + @authorization(filter: [{ where: { node: { from: { some: { id: { eq: "$jwt.sub" } } } } } }]) { + id: ID! @id + from: [User!]! + @relationship(type: "HAS_BLOCKED", direction: IN) + @settable(onCreate: true, onUpdate: false) + to: [User!]! + @relationship(type: "IS_BLOCKING", direction: OUT) + @settable(onCreate: true, onUpdate: false) } type Query { @@ -85,21 +95,19 @@ describe("https://github.com/neo4j/graphql/issues/5270", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { OPTIONAL MATCH (u:User {id: $jwt.sub}) RETURN u } WITH u AS this0 - CALL { - WITH this0 - MATCH (this0)-[:HAS_BLOCKED]->(this1:UserBlockedUser) - OPTIONAL MATCH (this1)-[:IS_BLOCKING]->(this2:User) - WITH *, count(this2) AS var3 - WITH * - WHERE (var3 <> 0 AND ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub)) - RETURN count(this1) > 0 AS var4 - } WITH * - WHERE ($isAuthenticated = true AND NOT (var4 = true)) + WHERE ($isAuthenticated = true AND NOT (EXISTS { + MATCH (this0)-[:HAS_BLOCKED]->(this1:UserBlockedUser) + WHERE EXISTS { + MATCH (this1)-[:IS_BLOCKING]->(this2:User) + WHERE ($jwt.sub IS NOT NULL AND this2.id = $jwt.sub) + } + })) WITH this0 { .id } AS this0 RETURN this0 AS this" `); diff --git a/packages/graphql/tests/tck/issues/5467.test.ts b/packages/graphql/tests/tck/issues/5467.test.ts index 8f88e72b40..2edbe822d7 100644 --- a/packages/graphql/tests/tck/issues/5467.test.ts +++ b/packages/graphql/tests/tck/issues/5467.test.ts @@ -66,7 +66,8 @@ describe("https://github.com/neo4j/graphql/issues/5467", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { MERGE (t:Test {name: $param0}) SET t.groups = $param1 return t } diff --git a/packages/graphql/tests/tck/issues/5497.test.ts b/packages/graphql/tests/tck/issues/5497.test.ts deleted file mode 100644 index 54e7e889ce..0000000000 --- a/packages/graphql/tests/tck/issues/5497.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/5497", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type User - @node - @authorization( - validate: [ - { operations: [CREATE, DELETE], where: { jwt: { roles_INCLUDES: "admin" } } } - { operations: [READ, UPDATE], where: { node: { id_EQ: "$jwt.sub" } } } - ] - filter: [{ where: { node: { id_EQ: "$jwt.sub" } } }] - ) { - id: ID! - cabinets: [Cabinet!]! @relationship(type: "HAS_CABINET", direction: OUT) - } - - type Cabinet @authorization(filter: [{ where: { node: { user: { id_EQ: "$jwt.sub" } } } }]) @node { - id: ID! @id - categories: [Category!]! @relationship(type: "HAS_CATEGORY", direction: OUT) - user: User! @relationship(type: "HAS_CABINET", direction: IN) - } - - type Category - @authorization(filter: [{ where: { node: { cabinet: { user: { id_EQ: "$jwt.sub" } } } } }]) - @node { - id: ID! @id - files: [File!]! @relationship(type: "HAS_FILE", direction: OUT) - cabinet: Cabinet! @relationship(type: "HAS_CATEGORY", direction: IN) - } - - type File @node { - id: ID! @unique - category: Category @relationship(type: "HAS_FILE", direction: IN) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("should properly add where filters for auth", async () => { - const query = /* GraphQL */ ` - mutation ($fileId: ID!, $newCategoryId: ID) { - updateFiles( - where: { id_EQ: $fileId } - update: { - category: { - disconnect: { where: { node: { NOT: { id_EQ: $newCategoryId } } } } - connect: { where: { node: { id_EQ: $newCategoryId } } } - } - } - ) { - info { - relationshipsDeleted - relationshipsCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - variableValues: { - fileId: "old-id", - newCategoryId: "new-id", - }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:File) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_category0_disconnect0_rel:HAS_FILE]-(this_category0_disconnect0:Category) - CALL { - WITH this_category0_disconnect0 - MATCH (this_category0_disconnect0)<-[:HAS_CATEGORY]-(authorization__before_this1:Cabinet) - OPTIONAL MATCH (authorization__before_this1)<-[:HAS_CABINET]-(authorization__before_this2:User) - WITH *, count(authorization__before_this2) AS authorization__before_var3 - WITH * - WHERE (authorization__before_var3 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this2.id = $jwt.sub)) - RETURN count(authorization__before_this1) = 1 AS authorization__before_var0 - } - WITH * - WHERE NOT (this_category0_disconnect0.id = $updateFiles_args_update_category_disconnect_where_Category_this_category0_disconnect0param0) AND ($isAuthenticated = true AND authorization__before_var0 = true) - CALL { - WITH this_category0_disconnect0, this_category0_disconnect0_rel, this - WITH collect(this_category0_disconnect0) as this_category0_disconnect0, this_category0_disconnect0_rel, this - UNWIND this_category0_disconnect0 as x - DELETE this_category0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_category0_disconnect_Category - } - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_category0_connect0_node:Category) - CALL { - WITH this_category0_connect0_node - MATCH (this_category0_connect0_node)<-[:HAS_CATEGORY]-(authorization__before_this1:Cabinet) - OPTIONAL MATCH (authorization__before_this1)<-[:HAS_CABINET]-(authorization__before_this2:User) - WITH *, count(authorization__before_this2) AS authorization__before_var3 - WITH * - WHERE (authorization__before_var3 <> 0 AND ($jwt.sub IS NOT NULL AND authorization__before_this2.id = $jwt.sub)) - RETURN count(authorization__before_this1) = 1 AS authorization__before_var0 - } - WITH * - WHERE this_category0_connect0_node.id = $this_category0_connect0_node_param0 AND ($isAuthenticated = true AND authorization__before_var0 = true) - CALL { - WITH * - WITH collect(this_category0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_category0_connect0_node - MERGE (this)<-[:HAS_FILE]-(this_category0_connect0_node) - } - } - WITH this, this_category0_connect0_node - RETURN count(*) AS connect_this_category0_connect_Category0 - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_category_Category_unique:HAS_FILE]-(:Category) - WITH count(this_category_Category_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDFile.category must be less than or equal to one', [0]) - RETURN c AS this_category_Category_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"old-id\\", - \\"updateFiles_args_update_category_disconnect_where_Category_this_category0_disconnect0param0\\": \\"new-id\\", - \\"isAuthenticated\\": false, - \\"jwt\\": {}, - \\"this_category0_connect0_node_param0\\": \\"new-id\\", - \\"updateFiles\\": { - \\"args\\": { - \\"update\\": { - \\"category\\": { - \\"connect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"new-id\\" - } - }, - \\"overwrite\\": true - }, - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"NOT\\": { - \\"id_EQ\\": \\"new-id\\" - } - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/5515.test.ts b/packages/graphql/tests/tck/issues/5515.test.ts index d13832dbde..65a43faeee 100644 --- a/packages/graphql/tests/tck/issues/5515.test.ts +++ b/packages/graphql/tests/tck/issues/5515.test.ts @@ -34,32 +34,36 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { @node @authorization( validate: [ - { operations: [CREATE, DELETE], where: { jwt: { roles_INCLUDES: "admin" } } } - { operations: [READ, UPDATE], where: { node: { id_EQ: "$jwt.sub" } } } + { operations: [CREATE, DELETE], where: { jwt: { roles: { includes: "admin" } } } } + { operations: [READ, UPDATE], where: { node: { id: { eq: "$jwt.sub" } } } } ] - filter: [{ where: { node: { id: "$jwt.sub" } } }] + filter: [{ where: { node: { id: { eq: "$jwt.sub" } } } }] ) { id: ID! cabinets: [Cabinet!]! @relationship(type: "HAS_CABINET", direction: OUT) } - type Cabinet @authorization(filter: [{ where: { node: { user: { id_EQ: "$jwt.sub" } } } }]) @node { + type Cabinet + @authorization(filter: [{ where: { node: { user: { some: { id: { eq: "$jwt.sub" } } } } } }]) + @node { id: ID! @id categories: [Category!]! @relationship(type: "HAS_CATEGORY", direction: OUT) - user: User! @relationship(type: "HAS_CABINET", direction: IN) + user: [User!]! @relationship(type: "HAS_CABINET", direction: IN) } type Category - @authorization(filter: [{ where: { node: { cabinet: { user: { id_EQ: "$jwt.sub" } } } } }]) + @authorization( + filter: [{ where: { node: { cabinet: { some: { user: { some: { id: { eq: "$jwt.sub" } } } } } } } }] + ) @node { id: ID! @id files: [File!]! @relationship(type: "HAS_FILE", direction: OUT) - cabinet: Cabinet! @relationship(type: "HAS_CATEGORY", direction: IN) + cabinet: [Cabinet!]! @relationship(type: "HAS_CATEGORY", direction: IN) } type File @node { - id: ID! @unique - category: Category @relationship(type: "HAS_FILE", direction: IN) + id: ID! + category: [Category!]! @relationship(type: "HAS_FILE", direction: IN) } `; @@ -71,7 +75,7 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { test("should delete categories with auth filters", async () => { const query = /* GraphQL */ ` mutation { - deleteCategories(where: { id_EQ: "category-video" }) { + deleteCategories(where: { id: { eq: "category-video" } }) { __typename nodesDeleted relationshipsDeleted @@ -82,26 +86,23 @@ describe("https://github.com/neo4j/graphql/issues/5515", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Category) - CALL { - WITH this + "CYPHER 5 + MATCH (this:Category) + WHERE (this.id = $param0 AND ($isAuthenticated = true AND EXISTS { MATCH (this)<-[:HAS_CATEGORY]-(this0:Cabinet) - OPTIONAL MATCH (this0)<-[:HAS_CABINET]-(this1:User) - WITH *, count(this1) AS var2 - WITH * - WHERE (var2 <> 0 AND ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub)) - RETURN count(this0) = 1 AS var3 - } - WITH * - WHERE (this.id = $param1 AND ($isAuthenticated = true AND var3 = true)) + WHERE EXISTS { + MATCH (this0)<-[:HAS_CABINET]-(this1:User) + WHERE ($jwt.sub IS NOT NULL AND this1.id = $jwt.sub) + } + })) DETACH DELETE this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"jwt\\": {}, - \\"param1\\": \\"category-video\\", - \\"isAuthenticated\\": false + \\"param0\\": \\"category-video\\", + \\"isAuthenticated\\": false, + \\"jwt\\": {} }" `); }); diff --git a/packages/graphql/tests/tck/issues/5599.test.ts b/packages/graphql/tests/tck/issues/5599.test.ts index 7deb0ba692..e1635c602e 100644 --- a/packages/graphql/tests/tck/issues/5599.test.ts +++ b/packages/graphql/tests/tck/issues/5599.test.ts @@ -53,7 +53,7 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { const query = /* GraphQL */ ` mutation { updateMovies( - update: { actors: { LeadActor: [{ delete: [{ where: { node: { name: "Actor1" } } }] }] } } + update: { actors: { LeadActor: [{ delete: [{ where: { node: { name: { eq: "Actor1" } } } }] }] } } ) { movies { title @@ -65,7 +65,8 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * CALL { WITH * @@ -94,7 +95,9 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Actor1\\" + \\"name\\": { + \\"eq\\": \\"Actor1\\" + } } } } @@ -116,8 +119,8 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { updateMovies( update: { actors: { - LeadActor: [{ delete: [{ where: { node: { name: "Actor1" } } }] }] - Extra: [{ delete: [{ where: { node: { name: "Actor2" } } }] }] + LeadActor: [{ delete: [{ where: { node: { name: { eq: "Actor1" } } } }] }] + Extra: [{ delete: [{ where: { node: { name: { eq: "Actor2" } } } }] }] } } ) { @@ -131,7 +134,8 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * CALL { WITH * @@ -173,7 +177,9 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Actor1\\" + \\"name\\": { + \\"eq\\": \\"Actor1\\" + } } } } @@ -186,7 +192,9 @@ describe("https://github.com/neo4j/graphql/issues/5599", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Actor2\\" + \\"name\\": { + \\"eq\\": \\"Actor2\\" + } } } } diff --git a/packages/graphql/tests/tck/issues/5635.test.ts b/packages/graphql/tests/tck/issues/5635.test.ts deleted file mode 100644 index 3446fc8c16..0000000000 --- a/packages/graphql/tests/tck/issues/5635.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/5635", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type Owner @node { - id: ID! @unique @id - owns: [MyNode!]! @relationship(type: "OWNS", direction: OUT) - } - - type MyNode - @node - @authorization( - validate: [ - { - operations: [READ, UPDATE, DELETE, DELETE_RELATIONSHIP, CREATE_RELATIONSHIP] - where: { node: { owner: { id: "$jwt.sub" } } } - when: [AFTER] - } - ] - ) { - id: ID! @unique @id - name: String! - owner: Owner! @relationship(type: "OWNS", direction: IN) - } - `; - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { key: "secret" }, - }, - }); - }); - - test("validation should applied correctly without causing cypher errors", async () => { - const mutation = /* GraphQL */ ` - mutation { - createMyNodes( - input: [ - { - name: "Test" - owner: { connectOrCreate: { onCreate: { node: {} }, where: { node: { id: "abc" } } } } - } - ] - ) { - myNodes { - id - name - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:MyNode) - SET this0.id = randomUUID() - SET this0.name = $this0_name - WITH this0 - CALL { - WITH this0 - MERGE (this0_owner_connectOrCreate0:Owner { id: $this0_owner_connectOrCreate_param0 }) - MERGE (this0)<-[this0_owner_connectOrCreate_this0:OWNS]-(this0_owner_connectOrCreate0) - WITH * - OPTIONAL MATCH (this0)<-[:OWNS]-(this0_owner_connectOrCreate_this1:Owner) - WITH *, count(this0_owner_connectOrCreate_this1) AS this0_owner_connectOrCreate_var2 - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND (this0_owner_connectOrCreate_var2 <> 0 AND ($jwt.sub IS NOT NULL AND this0_owner_connectOrCreate_this1.id = $jwt.sub))), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_owner_Owner_unique:OWNS]-(:Owner) - WITH count(this0_owner_Owner_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMyNode.owner required exactly once', [0]) - RETURN c AS this0_owner_Owner_unique_ignored - } - RETURN this0 - } - CALL { - WITH this0 - RETURN this0 { .id, .name } AS create_var0 - } - RETURN [create_var0] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_name\\": \\"Test\\", - \\"this0_owner_connectOrCreate_param0\\": \\"abc\\", - \\"isAuthenticated\\": false, - \\"jwt\\": {}, - \\"resolvedCallbacks\\": {} - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/582.test.ts b/packages/graphql/tests/tck/issues/582.test.ts index bc867b4801..df8e51deff 100644 --- a/packages/graphql/tests/tck/issues/582.test.ts +++ b/packages/graphql/tests/tck/issues/582.test.ts @@ -54,13 +54,17 @@ describe("#582", () => { const result = await translateQuery(neoSchema, query, { variableValues: { where: { - type_EQ: "Cat", - childrenConnection_SOME: { - node: { - type_EQ: "Dog", - parentsConnection_SOME: { - node: { - type_EQ: "Bird", + type: { eq: "Cat" }, + childrenConnection: { + some: { + node: { + type: { eq: "Dog" }, + parentsConnection: { + some: { + node: { + type: { eq: "Bird" }, + }, + }, }, }, }, @@ -70,7 +74,8 @@ describe("#582", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Entity) + "CYPHER 5 + MATCH (this:Entity) WHERE (this.type = $param0 AND EXISTS { MATCH (this)-[this0:EDGE]->(this1:Entity) WHERE (this1.type = $param1 AND EXISTS { @@ -102,16 +107,22 @@ describe("#582", () => { const result = await translateQuery(neoSchema, query, { variableValues: { where: { - type_EQ: "Cat", - childrenConnection_SOME: { - node: { - type_EQ: "Dog", - parentsConnection_SOME: { - node: { - type_EQ: "Bird", - childrenConnection_SOME: { + type: { eq: "Cat" }, + childrenConnection: { + some: { + node: { + type: { eq: "Dog" }, + parentsConnection: { + some: { node: { - type_EQ: "Fish", + type: { eq: "Bird" }, + childrenConnection: { + some: { + node: { + type: { eq: "Fish" }, + }, + }, + }, }, }, }, @@ -123,7 +134,8 @@ describe("#582", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Entity) + "CYPHER 5 + MATCH (this:Entity) WHERE (this.type = $param0 AND EXISTS { MATCH (this)-[this0:EDGE]->(this1:Entity) WHERE (this1.type = $param1 AND EXISTS { diff --git a/packages/graphql/tests/tck/issues/583.test.ts b/packages/graphql/tests/tck/issues/583.test.ts index fae0eee917..bdf767b710 100644 --- a/packages/graphql/tests/tck/issues/583.test.ts +++ b/packages/graphql/tests/tck/issues/583.test.ts @@ -79,7 +79,8 @@ describe("https://github.com/neo4j/graphql/issues/583", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/5887.test.ts b/packages/graphql/tests/tck/issues/5887.test.ts deleted file mode 100644 index ec0f9ba718..0000000000 --- a/packages/graphql/tests/tck/issues/5887.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("https://github.com/neo4j/graphql/issues/5887", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - interface Base { - id: ID! - } - - type A implements Base { - id: ID! - } - - type B implements Base { - id: ID! - } - - type Test { - base: Base! @relationship(type: "HAS", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { - authorization: { key: "secret" }, - }, - }); - }); - - test("should return relationship when first interface match", async () => { - const query = /* GraphQL */ ` - query { - tests(where: { base: { id: "1" } }) { - base { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) - OPTIONAL MATCH (this)-[:HAS]->(this0:A) - WITH *, count(this0) AS var1 - OPTIONAL MATCH (this)-[:HAS]->(this2:B) - WITH *, count(this2) AS var3 - WITH * - WHERE ((var1 <> 0 AND this0.id = $param0) OR (var3 <> 0 AND this2.id = $param1)) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this4:HAS]->(this5:A) - WITH this5 { .id, __resolveType: \\"A\\", __id: id(this5) } AS this5 - RETURN this5 AS var6 - UNION - WITH * - MATCH (this)-[this7:HAS]->(this8:B) - WITH this8 { .id, __resolveType: \\"B\\", __id: id(this8) } AS this8 - RETURN this8 AS var6 - } - WITH var6 - RETURN head(collect(var6)) AS var6 - } - RETURN this { base: var6 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"1\\", - \\"param1\\": \\"1\\" - }" - `); - }); - - test("should return relationship when second interface match", async () => { - const query = /* GraphQL */ ` - query { - tests(where: { base: { id: "2" } }) { - base { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) - OPTIONAL MATCH (this)-[:HAS]->(this0:A) - WITH *, count(this0) AS var1 - OPTIONAL MATCH (this)-[:HAS]->(this2:B) - WITH *, count(this2) AS var3 - WITH * - WHERE ((var1 <> 0 AND this0.id = $param0) OR (var3 <> 0 AND this2.id = $param1)) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this4:HAS]->(this5:A) - WITH this5 { .id, __resolveType: \\"A\\", __id: id(this5) } AS this5 - RETURN this5 AS var6 - UNION - WITH * - MATCH (this)-[this7:HAS]->(this8:B) - WITH this8 { .id, __resolveType: \\"B\\", __id: id(this8) } AS this8 - RETURN this8 AS var6 - } - WITH var6 - RETURN head(collect(var6)) AS var6 - } - RETURN this { base: var6 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"2\\", - \\"param1\\": \\"2\\" - }" - `); - }); - - test("should not return relationship when no interface match", async () => { - const query = /* GraphQL */ ` - query { - tests(where: { base: { id: "x" } }) { - base { - id - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Test) - OPTIONAL MATCH (this)-[:HAS]->(this0:A) - WITH *, count(this0) AS var1 - OPTIONAL MATCH (this)-[:HAS]->(this2:B) - WITH *, count(this2) AS var3 - WITH * - WHERE ((var1 <> 0 AND this0.id = $param0) OR (var3 <> 0 AND this2.id = $param1)) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this4:HAS]->(this5:A) - WITH this5 { .id, __resolveType: \\"A\\", __id: id(this5) } AS this5 - RETURN this5 AS var6 - UNION - WITH * - MATCH (this)-[this7:HAS]->(this8:B) - WITH this8 { .id, __resolveType: \\"B\\", __id: id(this8) } AS this8 - RETURN this8 AS var6 - } - WITH var6 - RETURN head(collect(var6)) AS var6 - } - RETURN this { base: var6 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"x\\", - \\"param1\\": \\"x\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/601.test.ts b/packages/graphql/tests/tck/issues/601.test.ts deleted file mode 100644 index dbae17d807..0000000000 --- a/packages/graphql/tests/tck/issues/601.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { createBearerToken } from "../../utils/create-bearer-token"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("#601", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - typeDefs = /* GraphQL */ ` - type JWT @jwt { - roles: [String!]! - } - - type UploadedDocument @relationshipProperties { - fileId: ID! - uploadedAt: DateTime! - } - - type Document @mutation(operations: []) @node { - id: ID! @id @unique - stakeholder: Stakeholder! @relationship(type: "REQUIRES", direction: OUT) - - customerContact: CustomerContact! - @relationship(type: "UPLOADED", properties: "UploadedDocument", direction: IN) - } - - extend type Document @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "view" } } }]) - - type CustomerContact @mutation(operations: []) @node { - email: String! - firstname: String! - lastname: String! - documents: [Document!]! @relationship(type: "UPLOADED", properties: "UploadedDocument", direction: OUT) - } - - extend type CustomerContact @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "view" } } }]) - - type Stakeholder @mutation(operations: []) @node { - id: ID! - fields: String! - documents: [Document!]! @relationship(type: "REQUIRES", direction: OUT) - } - - extend type Stakeholder @authorization(validate: [{ where: { jwt: { roles_INCLUDES: "view" } } }]) - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - features: { authorization: { key: "secret" } }, - }); - }); - - test("Example 1", async () => { - const query = /* GraphQL */ ` - query Document { - stakeholders { - documents { - customerContactConnection { - edges { - properties { - fileId - uploadedAt - } - } - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query, { - contextValues: { token: createBearerToken("secret") }, - }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Stakeholder) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH this - MATCH (this)-[this0:REQUIRES]->(this1:Document) - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH this1 - MATCH (this1)<-[this2:UPLOADED]-(this3:CustomerContact) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $param4 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH collect({ node: this3, relationship: this2 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this3, edge.relationship AS this2 - RETURN collect({ properties: { fileId: this2.fileId, uploadedAt: apoc.date.convertFormat(toString(this2.uploadedAt), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\"), __resolveType: \\"UploadedDocument\\" }, node: { __id: id(this3), __resolveType: \\"CustomerContact\\" } }) AS var4 - } - RETURN { edges: var4, totalCount: totalCount } AS var5 - } - WITH this1 { customerContactConnection: var5 } AS this1 - RETURN collect(this1) AS var6 - } - RETURN this { documents: var6 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [] - }, - \\"param2\\": \\"view\\", - \\"param3\\": \\"view\\", - \\"param4\\": \\"view\\" - }" - `); - }); -}); diff --git a/packages/graphql/tests/tck/issues/630.test.ts b/packages/graphql/tests/tck/issues/630.test.ts index 2f3063ba49..3c76c85df6 100644 --- a/packages/graphql/tests/tck/issues/630.test.ts +++ b/packages/graphql/tests/tck/issues/630.test.ts @@ -66,7 +66,8 @@ describe("Cypher directive", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/832.test.ts b/packages/graphql/tests/tck/issues/832.test.ts index 61d3c3ec67..352c9df1e7 100644 --- a/packages/graphql/tests/tck/issues/832.test.ts +++ b/packages/graphql/tests/tck/issues/832.test.ts @@ -31,17 +31,17 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { } type Person implements Entity @node { - id: String! @unique + id: String! name: String! } type Place implements Entity @node { - id: String! @unique + id: String! location: Point! } type Interaction @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) @@ -59,14 +59,14 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { createInteractions( input: [ { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["cain"] } } } } + objects: { connect: { where: { node: { id: { in: ["cain"] } } } } } } { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["abel"] } } } } + objects: { connect: { where: { node: { id: { in: ["abel"] } } } } } } ] ) { @@ -83,7 +83,8 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Interaction) SET this0.id = randomUUID() SET this0.kind = $this0_kind @@ -99,7 +100,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) } } WITH this0, this0_subjects_connect0_node @@ -116,7 +117,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect1_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) } } WITH this0, this0_subjects_connect1_node @@ -134,7 +135,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect0_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) } } WITH this0, this0_objects_connect0_node @@ -151,7 +152,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect1_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) } } WITH this0, this0_objects_connect1_node @@ -175,7 +176,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_subjects_connect0_node - MERGE (this1)<-[:ACTED_IN]-(this1_subjects_connect0_node) + CREATE (this1)<-[:ACTED_IN]-(this1_subjects_connect0_node) } } WITH this1, this1_subjects_connect0_node @@ -192,7 +193,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_subjects_connect1_node - MERGE (this1)<-[:ACTED_IN]-(this1_subjects_connect1_node) + CREATE (this1)<-[:ACTED_IN]-(this1_subjects_connect1_node) } } WITH this1, this1_subjects_connect1_node @@ -210,7 +211,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_objects_connect0_node - MERGE (this1)-[:ACTED_IN]->(this1_objects_connect0_node) + CREATE (this1)-[:ACTED_IN]->(this1_objects_connect0_node) } } WITH this1, this1_objects_connect0_node @@ -227,7 +228,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_objects_connect1_node - MERGE (this1)-[:ACTED_IN]->(this1_objects_connect1_node) + CREATE (this1)-[:ACTED_IN]->(this1_objects_connect1_node) } } WITH this1, this1_objects_connect1_node @@ -289,9 +290,9 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { createInteractions( input: [ { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["cain"] } } } } + objects: { connect: { where: { node: { id: { in: ["cain"] } } } } } } ] ) { @@ -308,7 +309,8 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Interaction) SET this0.id = randomUUID() SET this0.kind = $this0_kind @@ -324,7 +326,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) } } WITH this0, this0_subjects_connect0_node @@ -341,7 +343,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect1_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) } } WITH this0, this0_subjects_connect1_node @@ -359,7 +361,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect0_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) } } WITH this0, this0_objects_connect0_node @@ -376,7 +378,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect1_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) } } WITH this0, this0_objects_connect1_node @@ -419,9 +421,9 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { createInteractions( input: [ { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["abel"] } } } } + objects: { connect: { where: { node: { id: { in: ["abel"] } } } } } } ] ) { @@ -438,7 +440,8 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Interaction) SET this0.id = randomUUID() SET this0.kind = $this0_kind @@ -454,7 +457,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) } } WITH this0, this0_subjects_connect0_node @@ -471,7 +474,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect1_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) } } WITH this0, this0_subjects_connect1_node @@ -489,7 +492,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect0_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) } } WITH this0, this0_objects_connect0_node @@ -506,7 +509,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect1_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) } } WITH this0, this0_objects_connect1_node @@ -549,14 +552,14 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { createInteractions( input: [ { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["cain"] } } } } + objects: { connect: { where: { node: { id: { in: ["cain"] } } } } } } { - subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } } + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } kind: "PARENT_OF" - objects: { connect: { where: { node: { id_IN: ["abel"] } } } } + objects: { connect: { where: { node: { id: { in: ["abel"] } } } } } } ] ) { @@ -579,7 +582,8 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Interaction) SET this0.id = randomUUID() SET this0.kind = $this0_kind @@ -595,7 +599,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) } } WITH this0, this0_subjects_connect0_node @@ -612,7 +616,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect1_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) } } WITH this0, this0_subjects_connect1_node @@ -630,7 +634,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect0_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect0_node) } } WITH this0, this0_objects_connect0_node @@ -647,7 +651,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_objects_connect1_node - MERGE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) + CREATE (this0)-[:ACTED_IN]->(this0_objects_connect1_node) } } WITH this0, this0_objects_connect1_node @@ -671,7 +675,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_subjects_connect0_node - MERGE (this1)<-[:ACTED_IN]-(this1_subjects_connect0_node) + CREATE (this1)<-[:ACTED_IN]-(this1_subjects_connect0_node) } } WITH this1, this1_subjects_connect0_node @@ -688,7 +692,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_subjects_connect1_node - MERGE (this1)<-[:ACTED_IN]-(this1_subjects_connect1_node) + CREATE (this1)<-[:ACTED_IN]-(this1_subjects_connect1_node) } } WITH this1, this1_subjects_connect1_node @@ -706,7 +710,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_objects_connect0_node - MERGE (this1)-[:ACTED_IN]->(this1_objects_connect0_node) + CREATE (this1)-[:ACTED_IN]->(this1_objects_connect0_node) } } WITH this1, this1_objects_connect0_node @@ -723,7 +727,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_objects_connect1_node - MERGE (this1)-[:ACTED_IN]->(this1_objects_connect1_node) + CREATE (this1)-[:ACTED_IN]->(this1_objects_connect1_node) } } WITH this1, this1_objects_connect1_node @@ -848,7 +852,10 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { mutation { createInteractions( input: [ - { subjects: { connect: { where: { node: { id_IN: ["adam", "eve"] } } } }, kind: "PARENT_OF" } + { + subjects: { connect: { where: { node: { id: { in: ["adam", "eve"] } } } } } + kind: "PARENT_OF" + } { kind: "PARENT_OF" } ] ) { @@ -865,7 +872,8 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Interaction) SET this0.id = randomUUID() SET this0.kind = $this0_kind @@ -881,7 +889,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect0_node) } } WITH this0, this0_subjects_connect0_node @@ -898,7 +906,7 @@ describe("https://github.com/neo4j/graphql/issues/832", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_subjects_connect1_node - MERGE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) + CREATE (this0)<-[:ACTED_IN]-(this0_subjects_connect1_node) } } WITH this0, this0_subjects_connect1_node diff --git a/packages/graphql/tests/tck/issues/847.test.ts b/packages/graphql/tests/tck/issues/847.test.ts index 5bc89d2de4..a74b242021 100644 --- a/packages/graphql/tests/tck/issues/847.test.ts +++ b/packages/graphql/tests/tck/issues/847.test.ts @@ -28,17 +28,17 @@ describe("https://github.com/neo4j/graphql/issues/847", () => { } type Person implements Entity @node { - id: String! @unique + id: String! name: String! } type Place implements Entity @node { - id: String! @unique + id: String! location: Point! } type Interaction @node { - id: ID! @id @unique + id: ID! @id kind: String! subjects: [Entity!]! @relationship(type: "ACTED_IN", direction: IN) objects: [Entity!]! @relationship(type: "ACTED_IN", direction: OUT) @@ -66,7 +66,8 @@ describe("https://github.com/neo4j/graphql/issues/847", () => { const result = await translateQuery(neoSchema, query, {}); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Interaction) + "CYPHER 5 + MATCH (this:Interaction) CALL { WITH this CALL { diff --git a/packages/graphql/tests/tck/issues/894.test.ts b/packages/graphql/tests/tck/issues/894.test.ts index a78f233ec9..63b52bf079 100644 --- a/packages/graphql/tests/tck/issues/894.test.ts +++ b/packages/graphql/tests/tck/issues/894.test.ts @@ -27,13 +27,13 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type User @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! - activeOrganization: Organization @relationship(type: "ACTIVELY_MANAGING", direction: OUT) + activeOrganization: [Organization!]! @relationship(type: "ACTIVELY_MANAGING", direction: OUT) } type Organization @node { - id: ID! @id @unique @alias(property: "_id") + id: ID! @id @alias(property: "_id") name: String! } `; @@ -47,11 +47,11 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { const query = /* GraphQL */ ` mutation SwapSides { updateUsers( - where: { name_EQ: "Luke Skywalker" } + where: { name: { eq: "Luke Skywalker" } } update: { activeOrganization: { - connect: { where: { node: { id_EQ: "test-id" } } } - disconnect: { where: { node: { NOT: { id_EQ: "test-id" } } } } + connect: { where: { node: { id: { eq: "test-id" } } } } + disconnect: { where: { node: { NOT: { id: { eq: "test-id" } } } } } } } ) { @@ -65,13 +65,14 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE this.name = $param0 WITH this CALL { WITH this OPTIONAL MATCH (this)-[this_activeOrganization0_disconnect0_rel:ACTIVELY_MANAGING]->(this_activeOrganization0_disconnect0:Organization) - WHERE NOT (this_activeOrganization0_disconnect0._id = $updateUsers_args_update_activeOrganization_disconnect_where_Organization_this_activeOrganization0_disconnect0param0) + WHERE NOT (this_activeOrganization0_disconnect0._id = $updateUsers_args_update_activeOrganization0_disconnect0_where_Organization_this_activeOrganization0_disconnect0param0) CALL { WITH this_activeOrganization0_disconnect0, this_activeOrganization0_disconnect0_rel, this WITH collect(this_activeOrganization0_disconnect0) as this_activeOrganization0_disconnect0, this_activeOrganization0_disconnect0_rel, this @@ -92,50 +93,51 @@ describe("https://github.com/neo4j/graphql/issues/894", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_activeOrganization0_connect0_node - MERGE (this)-[:ACTIVELY_MANAGING]->(this_activeOrganization0_connect0_node) + CREATE (this)-[:ACTIVELY_MANAGING]->(this_activeOrganization0_connect0_node) } } WITH this, this_activeOrganization0_connect0_node RETURN count(*) AS connect_this_activeOrganization0_connect_Organization0 } - WITH * - CALL { - WITH this - MATCH (this)-[this_activeOrganization_Organization_unique:ACTIVELY_MANAGING]->(:Organization) - WITH count(this_activeOrganization_Organization_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDUser.activeOrganization must be less than or equal to one', [0]) - RETURN c AS this_activeOrganization_Organization_unique_ignored - } RETURN collect(DISTINCT this { id: this._id }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": \\"Luke Skywalker\\", - \\"updateUsers_args_update_activeOrganization_disconnect_where_Organization_this_activeOrganization0_disconnect0param0\\": \\"test-id\\", + \\"updateUsers_args_update_activeOrganization0_disconnect0_where_Organization_this_activeOrganization0_disconnect0param0\\": \\"test-id\\", \\"this_activeOrganization0_connect0_node_param0\\": \\"test-id\\", \\"updateUsers\\": { \\"args\\": { \\"update\\": { - \\"activeOrganization\\": { - \\"connect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"test-id\\" + \\"activeOrganization\\": [ + { + \\"connect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"test-id\\" + } + } + } } - }, - \\"overwrite\\": true - }, - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"NOT\\": { - \\"id_EQ\\": \\"test-id\\" + ], + \\"disconnect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"NOT\\": { + \\"id\\": { + \\"eq\\": \\"test-id\\" + } + } + } } } - } + ] } - } + ] } } }, diff --git a/packages/graphql/tests/tck/issues/901.test.ts b/packages/graphql/tests/tck/issues/901.test.ts index e41c43775c..15850c86b4 100644 --- a/packages/graphql/tests/tck/issues/901.test.ts +++ b/packages/graphql/tests/tck/issues/901.test.ts @@ -27,10 +27,11 @@ describe("https://github.com/neo4j/graphql/issues/901", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Series @node { - id: ID! @id @unique + id: ID! @id name: String! - brand: Series @relationship(type: "HAS_BRAND", direction: OUT, properties: "Properties") - manufacturer: Series @relationship(type: "HAS_MANUFACTURER", direction: OUT, properties: "Properties") + brand: [Series!]! @relationship(type: "HAS_BRAND", direction: OUT, properties: "Properties") + manufacturer: [Series!]! + @relationship(type: "HAS_MANUFACTURER", direction: OUT, properties: "Properties") } type Properties @relationshipProperties { @@ -64,21 +65,25 @@ describe("https://github.com/neo4j/graphql/issues/901", () => { OR: [ { manufacturerConnection: { - edge: { - current_EQ: true, - }, - node: { - name_EQ: "abc", + some: { + edge: { + current: { eq: true }, + }, + node: { + name: { eq: "abc" }, + }, }, }, }, { brandConnection: { - edge: { - current_EQ: true, - }, - node: { - name_EQ: "smart", + some: { + edge: { + current: { eq: true }, + }, + node: { + name: { eq: "smart" }, + }, }, }, }, @@ -88,19 +93,28 @@ describe("https://github.com/neo4j/graphql/issues/901", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Series) - WHERE (single(this0 IN [(this)-[this1:HAS_MANUFACTURER]->(this0:Series) WHERE (this0.name = $param0 AND this1.current = $param1) | 1] WHERE true) OR single(this2 IN [(this)-[this3:HAS_BRAND]->(this2:Series) WHERE (this2.name = $param2 AND this3.current = $param3) | 1] WHERE true)) + "CYPHER 5 + MATCH (this:Series) + WHERE (EXISTS { + MATCH (this)-[this0:HAS_MANUFACTURER]->(this1:Series) + WHERE (this1.name = $param0 AND this0.current = $param1) + } OR EXISTS { + MATCH (this)-[this2:HAS_BRAND]->(this3:Series) + WHERE (this3.name = $param2 AND this2.current = $param3) + }) CALL { WITH this MATCH (this)-[this4:HAS_BRAND]->(this5:Series) + WITH DISTINCT this5 WITH this5 { .name } AS this5 - RETURN head(collect(this5)) AS var6 + RETURN collect(this5) AS var6 } CALL { WITH this MATCH (this)-[this7:HAS_MANUFACTURER]->(this8:Series) + WITH DISTINCT this8 WITH this8 { .name } AS this8 - RETURN head(collect(this8)) AS var9 + RETURN collect(this8) AS var9 } RETURN this { .name, brand: var6, manufacturer: var9 } AS this" `); diff --git a/packages/graphql/tests/tck/issues/988.test.ts b/packages/graphql/tests/tck/issues/988.test.ts index 6cb800a11c..536cebc783 100644 --- a/packages/graphql/tests/tck/issues/988.test.ts +++ b/packages/graphql/tests/tck/issues/988.test.ts @@ -56,7 +56,7 @@ describe("https://github.com/neo4j/graphql/issues/988", () => { test("where with multiple filters and params", async () => { const query = /* GraphQL */ ` - query getSeriesWithRelationFilters($where: SeriesWhere = { current: true }) { + query getSeriesWithRelationFilters($where: SeriesWhere = { current: { eq: true } }) { series(where: $where) { name current @@ -87,27 +87,31 @@ describe("https://github.com/neo4j/graphql/issues/988", () => { const result = await translateQuery(neoSchema, query, { variableValues: { where: { - current_EQ: true, + current: { eq: true }, AND: [ { OR: [ { - manufacturerConnection_SOME: { - edge: { - current_EQ: true, - }, - node: { - name_EQ: "C", + manufacturerConnection: { + some: { + edge: { + current: { eq: true }, + }, + node: { + name: { eq: "C" }, + }, }, }, }, { - manufacturerConnection_SOME: { - edge: { - current_EQ: false, - }, - node: { - name_EQ: "AM", + manufacturerConnection: { + some: { + edge: { + current: { eq: false }, + }, + node: { + name: { eq: "AM" }, + }, }, }, }, @@ -116,12 +120,14 @@ describe("https://github.com/neo4j/graphql/issues/988", () => { { OR: [ { - brandConnection_SOME: { - edge: { - current_EQ: true, - }, - node: { - name_EQ: "smart", + brandConnection: { + some: { + edge: { + current: { eq: true }, + }, + node: { + name: { eq: "smart" }, + }, }, }, }, @@ -133,7 +139,8 @@ describe("https://github.com/neo4j/graphql/issues/988", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Series) + "CYPHER 5 + MATCH (this:Series) WHERE (this.current = $param0 AND ((EXISTS { MATCH (this)-[this0:MANUFACTURER]->(this1:Manufacturer) WHERE (this1.name = $param1 AND this0.current = $param2) diff --git a/packages/graphql/tests/tck/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts b/packages/graphql/tests/tck/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts index 3234041d13..09ed603f34 100644 --- a/packages/graphql/tests/tck/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts +++ b/packages/graphql/tests/tck/issues/context-variable-not-always-resolved-on-cypher-queries.test.ts @@ -29,7 +29,7 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { @mutation(operations: []) @limit(default: 100, max: 300) { iri: ID! @id @alias(property: "uri") - realizationOf: Work @relationship(type: "realizationOf", direction: OUT) + realizationOf: [Work!]! @relationship(type: "realizationOf", direction: OUT) relToUnion: [unionTarget!]! @relationship(type: "relToUnion", direction: OUT) relToInterface: [interfaceTarget!]! @relationship(type: "relToInterface", direction: OUT) } @@ -72,7 +72,11 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { query { exprs( where: { - realizationOf: { hasResourceType_SOME: { iri_EQ: "http://data.somesite.com/crown/test-id" } } + realizationOf: { + some: { + hasResourceType: { some: { iri: { eq: "http://data.somesite.com/crown/test-id" } } } + } + } } limit: 1 ) { @@ -90,11 +94,15 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Exprlabel:test:Resource) - WHERE single(this0 IN [(this)-[:realizationOf]->(this0:WorkLabel:test:Resource) WHERE EXISTS { - MATCH (this0)-[:hasResourceType]->(this1:ResourceType) - WHERE this1.uri = $param0 - } | 1] WHERE true) + "CYPHER 5 + MATCH (this:Exprlabel:test:Resource) + WHERE EXISTS { + MATCH (this)-[:realizationOf]->(this0:WorkLabel:test:Resource) + WHERE EXISTS { + MATCH (this0)-[:hasResourceType]->(this1:ResourceType) + WHERE this1.uri = $param0 + } + } WITH * LIMIT $param1 RETURN this { iri: this.uri } AS this" @@ -117,7 +125,11 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { query { exprs( where: { - realizationOf: { hasResourceType_SOME: { iri_EQ: "http://data.somesite.com/crown/test-id" } } + realizationOf: { + some: { + hasResourceType: { some: { iri: { eq: "http://data.somesite.com/crown/test-id" } } } + } + } } limit: 1 ) { @@ -140,11 +152,15 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Exprlabel:test:Resource) - WHERE single(this0 IN [(this)-[:realizationOf]->(this0:WorkLabel:test:Resource) WHERE EXISTS { - MATCH (this0)-[:hasResourceType]->(this1:ResourceType) - WHERE this1.uri = $param0 - } | 1] WHERE true) + "CYPHER 5 + MATCH (this:Exprlabel:test:Resource) + WHERE EXISTS { + MATCH (this)-[:realizationOf]->(this0:WorkLabel:test:Resource) + WHERE EXISTS { + MATCH (this0)-[:hasResourceType]->(this1:ResourceType) + WHERE this1.uri = $param0 + } + } WITH * LIMIT $param1 CALL { @@ -183,7 +199,11 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { query { exprs( where: { - realizationOf: { hasResourceType_SOME: { iri_EQ: "http://data.somesite.com/crown/test-id" } } + realizationOf: { + some: { + hasResourceType: { some: { iri: { eq: "http://data.somesite.com/crown/test-id" } } } + } + } } limit: 1 ) { @@ -206,11 +226,15 @@ describe("context-variable-not-always-resolved-on-cypher-queries", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Exprlabel:test:Resource) - WHERE single(this0 IN [(this)-[:realizationOf]->(this0:WorkLabel:test:Resource) WHERE EXISTS { - MATCH (this0)-[:hasResourceType]->(this1:ResourceType) - WHERE this1.uri = $param0 - } | 1] WHERE true) + "CYPHER 5 + MATCH (this:Exprlabel:test:Resource) + WHERE EXISTS { + MATCH (this)-[:realizationOf]->(this0:WorkLabel:test:Resource) + WHERE EXISTS { + MATCH (this0)-[:hasResourceType]->(this1:ResourceType) + WHERE this1.uri = $param0 + } + } WITH * LIMIT $param1 CALL { diff --git a/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts b/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts index 4ea4608e15..6c7542cf13 100644 --- a/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts +++ b/packages/graphql/tests/tck/issues/missing-custom-cypher-on-unions.test.ts @@ -61,7 +61,8 @@ describe("Missing custom Cypher on unions", () => { iri: ID! @id @alias(property: "uri") relatesToChild: [hierarchicalNodeTarget!]! @relationship(type: "relatesToChild", properties: "RelateProps", direction: OUT) - isContained: HierarchicalRoot! @relationship(type: "isContained", properties: "RelateProps", direction: OUT) + isContained: [HierarchicalRoot!]! + @relationship(type: "isContained", properties: "RelateProps", direction: OUT) hierarchicalPathNodes: [choNode] @cypher( @@ -93,9 +94,9 @@ describe("Missing custom Cypher on unions", () => { type choNode @mutation(operations: []) @node { iri: ID! @id @alias(property: "uri") identifier: String - title: [title] + title: [title!] hasSortKey: String - labels: [String] + labels: [String!] partOf: ID partOfType: String } @@ -122,7 +123,9 @@ describe("Missing custom Cypher on unions", () => { test("should include checks for auth jwt param is not null", async () => { const query = /* GraphQL */ ` query browseHierarchicalComponents($hierarchicalRootId: ID!, $choNodeIris: [ID!]!) { - hierarchicalComponents(where: { isContained: { iri_EQ: $hierarchicalRootId }, iri_IN: $choNodeIris }) { + hierarchicalComponents( + where: { isContained: { some: { iri: { eq: $hierarchicalRootId } } }, iri: { in: $choNodeIris } } + ) { #...hierarchicalComponentFields relatesToChild { ...hierarchicalComponentFields @@ -149,28 +152,29 @@ describe("Missing custom Cypher on unions", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:HierarchicalComponent:Resource) - OPTIONAL MATCH (this)-[:isContained]->(this0:HierarchicalRoot:Resource) - WITH *, count(this0) AS var1 - WITH * - WHERE (this.uri IN $param0 AND (var1 <> 0 AND this0.uri = $param1)) + "CYPHER 5 + MATCH (this:HierarchicalComponent:Resource) + WHERE (this.uri IN $param0 AND EXISTS { + MATCH (this)-[:isContained]->(this0:HierarchicalRoot:Resource) + WHERE this0.uri = $param1 + }) WITH * LIMIT $param2 CALL { WITH this CALL { WITH * - MATCH (this)-[this2:relatesToChild]->(this3:HierarchicalRoot:Resource) - WITH this3 { __resolveType: \\"HierarchicalRoot\\", __id: id(this3) } AS this3 - RETURN this3 AS var4 + MATCH (this)-[this1:relatesToChild]->(this2:HierarchicalRoot:Resource) + WITH this2 { __resolveType: \\"HierarchicalRoot\\", __id: id(this2) } AS this2 + RETURN this2 AS var3 UNION WITH * - MATCH (this)-[this5:relatesToChild]->(this6:HierarchicalComponent:Resource) + MATCH (this)-[this4:relatesToChild]->(this5:HierarchicalComponent:Resource) CALL { - WITH this6 + WITH this5 CALL { - WITH this6 - WITH this6 AS this + WITH this5 + WITH this5 AS this MATCH p=(this)<-[:relatesToChild*..10]-(parent:HierarchicalRoot) WITH p, parent OPTIONAL MATCH (parent) - [:type] -> (parentType) @@ -192,32 +196,32 @@ describe("Missing custom Cypher on unions", () => { } AS obj RETURN DISTINCT obj as result } - WITH result AS this7 - WITH this7 { .hasSortKey, iri: this7.uri } AS this7 - RETURN collect(this7) AS var8 + WITH result AS this6 + WITH this6 { .hasSortKey, iri: this6.uri } AS this6 + RETURN collect(this6) AS var7 } - WITH this6 { hierarchicalPathNodes: var8, __resolveType: \\"HierarchicalComponent\\", __id: id(this6) } AS this6 - RETURN this6 AS var4 + WITH this5 { hierarchicalPathNodes: var7, __resolveType: \\"HierarchicalComponent\\", __id: id(this5) } AS this5 + RETURN this5 AS var3 UNION WITH * - MATCH (this)-[this9:relatesToChild]->(this10:Expression:MyTenant:Resource) - WITH this10 { __resolveType: \\"Expression\\", __id: id(this10) } AS this10 - RETURN this10 AS var4 + MATCH (this)-[this8:relatesToChild]->(this9:Expression:MyTenant:Resource) + WITH this9 { __resolveType: \\"Expression\\", __id: id(this9) } AS this9 + RETURN this9 AS var3 UNION WITH * - MATCH (this)-[this11:relatesToChild]->(this12:Work:MyTenant:Resource) - WITH this12 { __resolveType: \\"Work\\", __id: id(this12) } AS this12 - RETURN this12 AS var4 + MATCH (this)-[this10:relatesToChild]->(this11:Work:MyTenant:Resource) + WITH this11 { __resolveType: \\"Work\\", __id: id(this11) } AS this11 + RETURN this11 AS var3 UNION WITH * - MATCH (this)-[this13:relatesToChild]->(this14:Fragment:MyTenant:Resource) - WITH this14 { __resolveType: \\"Fragment\\", __id: id(this14) } AS this14 - RETURN this14 AS var4 + MATCH (this)-[this12:relatesToChild]->(this13:Fragment:MyTenant:Resource) + WITH this13 { __resolveType: \\"Fragment\\", __id: id(this13) } AS this13 + RETURN this13 AS var3 } - WITH var4 - RETURN collect(var4) AS var4 + WITH var3 + RETURN collect(var3) AS var3 } - RETURN this { relatesToChild: var4 } AS this" + RETURN this { relatesToChild: var3 } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` diff --git a/packages/graphql/tests/tck/math.test.ts b/packages/graphql/tests/tck/math.test.ts index 4cbb46dd89..286a3d5c36 100644 --- a/packages/graphql/tests/tck/math.test.ts +++ b/packages/graphql/tests/tck/math.test.ts @@ -32,11 +32,11 @@ describe("Math operators", () => { type Star implements Wife @node { marriageLength: Int - marriedWith: Actor @relationship(type: "MARRIED_WITH", direction: IN) + marriedWith: [Actor!]! @relationship(type: "MARRIED_WITH", direction: IN) } type Movie @node { - id: ID! @id @unique + id: ID! @id title: String! viewers: Int revenue: Float @@ -47,7 +47,7 @@ describe("Math operators", () => { id: ID! name: String! actedIn: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT) - marriedWith: Wife @relationship(type: "MARRIED_WITH", direction: OUT) + marriedWith: [Wife!]! @relationship(type: "MARRIED_WITH", direction: OUT) } type ActedIn @relationshipProperties { @@ -74,7 +74,8 @@ describe("Math operators", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this CALL { WITH this @@ -111,7 +112,8 @@ describe("Math operators", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this CALL { WITH this @@ -147,7 +149,8 @@ describe("Math operators", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -166,6 +169,7 @@ describe("Math operators", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .viewers } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -206,7 +210,8 @@ describe("Math operators", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -225,6 +230,7 @@ describe("Math operators", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -282,7 +288,8 @@ describe("Math operators", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WITH this CALL { WITH this @@ -298,14 +305,6 @@ describe("Math operators", () => { SET this_marriedWith0.marriageLength = this_marriedWith0.marriageLength + $this_update_marriedWith0_marriageLength_INCREMENT RETURN this_marriedWith0 as this_marriedWith0_marriageLength__INCREMENT } - WITH this, this_marriedWith0 - CALL { - WITH this_marriedWith0 - MATCH (this_marriedWith0)<-[this_marriedWith0_marriedWith_Actor_unique:MARRIED_WITH]-(:Actor) - WITH count(this_marriedWith0_marriedWith_Actor_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDStar.marriedWith must be less than or equal to one', [0]) - RETURN c AS this_marriedWith0_marriedWith_Actor_unique_ignored - } RETURN count(*) AS update_this_marriedWith0 } RETURN count(*) AS update_this_Star @@ -320,7 +319,7 @@ describe("Math operators", () => { RETURN update_this1 AS update_var2 } WITH update_var2 - RETURN head(collect(update_var2)) AS update_var2 + RETURN collect(update_var2) AS update_var2 } RETURN collect(DISTINCT this { .name, marriedWith: update_var2 }) AS data" `); diff --git a/packages/graphql/tests/tck/nested-unions.test.ts b/packages/graphql/tests/tck/nested-unions.test.ts index 576ee4d1e1..173bb9f3d1 100644 --- a/packages/graphql/tests/tck/nested-unions.test.ts +++ b/packages/graphql/tests/tck/nested-unions.test.ts @@ -60,13 +60,13 @@ describe("Nested Unions", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Movie" } + where: { title: { eq: "Movie" } } update: { actors: { LeadActor: { connect: { - where: { node: { name_EQ: "Actor" } } - connect: { actedIn: { Series: { where: { node: { name_EQ: "Series" } } } } } + where: { node: { name: { eq: "Actor" } } } + connect: { actedIn: { Series: { where: { node: { name: { eq: "Series" } } } } } } } } } @@ -92,7 +92,8 @@ describe("Nested Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { @@ -106,7 +107,7 @@ describe("Nested Unions", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors_LeadActor0_connect0_node - MERGE (this)<-[:ACTED_IN]-(this_actors_LeadActor0_connect0_node) + CREATE (this)<-[:ACTED_IN]-(this_actors_LeadActor0_connect0_node) } } WITH this, this_actors_LeadActor0_connect0_node @@ -121,7 +122,7 @@ describe("Nested Unions", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this_actors_LeadActor0_connect0_node UNWIND connectedNodes as this_actors_LeadActor0_connect0_node_actedIn_Series0_node - MERGE (this_actors_LeadActor0_connect0_node)-[:ACTED_IN]->(this_actors_LeadActor0_connect0_node_actedIn_Series0_node) + CREATE (this_actors_LeadActor0_connect0_node)-[:ACTED_IN]->(this_actors_LeadActor0_connect0_node_actedIn_Series0_node) } } WITH this, this_actors_LeadActor0_connect0_node, this_actors_LeadActor0_connect0_node_actedIn_Series0_node @@ -179,13 +180,13 @@ describe("Nested Unions", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "Movie" } + where: { title: { eq: "Movie" } } update: { actors: { LeadActor: { disconnect: { - where: { node: { name_EQ: "Actor" } } - disconnect: { actedIn: { Series: { where: { node: { name_EQ: "Series" } } } } } + where: { node: { name: { eq: "Actor" } } } + disconnect: { actedIn: { Series: { where: { node: { name: { eq: "Series" } } } } } } } } } @@ -211,7 +212,8 @@ describe("Nested Unions", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { @@ -289,7 +291,9 @@ describe("Nested Unions", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor\\" + \\"name\\": { + \\"eq\\": \\"Actor\\" + } } }, \\"disconnect\\": { @@ -298,7 +302,9 @@ describe("Nested Unions", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Series\\" + \\"name\\": { + \\"eq\\": \\"Series\\" + } } } } diff --git a/packages/graphql/tests/tck/null.test.ts b/packages/graphql/tests/tck/null.test.ts index 2747bacb07..c47c79d289 100644 --- a/packages/graphql/tests/tck/null.test.ts +++ b/packages/graphql/tests/tck/null.test.ts @@ -47,7 +47,7 @@ describe("Cypher NULL", () => { test("Simple IS NULL", async () => { const query = /* GraphQL */ ` query { - movies(where: { title_EQ: null }) { + movies(where: { title: { eq: null } }) { title } } @@ -56,7 +56,8 @@ describe("Cypher NULL", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title IS NULL RETURN this { .title } AS this" `); @@ -67,7 +68,7 @@ describe("Cypher NULL", () => { test("Simple IS NOT NULL", async () => { const query = /* GraphQL */ ` query { - movies(where: { NOT: { title_EQ: null } }) { + movies(where: { NOT: { title: { eq: null } } }) { title } } @@ -76,55 +77,12 @@ describe("Cypher NULL", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (this.title IS NULL) RETURN this { .title } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); }); - - test("Simple relationship IS NULL", async () => { - const query = /* GraphQL */ ` - query { - movies(where: { actors_SOME: null }) { - title - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE NOT (EXISTS { - MATCH (this)<-[:ACTED_IN]-(this0:Actor) - }) - RETURN this { .title } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("Simple relationship IS NOT NULL", async () => { - const query = /* GraphQL */ ` - query { - movies(where: { NOT: { actors_SOME: null } }) { - title - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE NOT (NOT (EXISTS { - MATCH (this)<-[:ACTED_IN]-(this0:Actor) - })) - RETURN this { .title } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); }); diff --git a/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts index de4ff84407..42af5fe9c4 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create-auth.test.ts @@ -31,20 +31,22 @@ describe("Batch Create, Auth", () => { roles: [String!]! } - type Actor @authorization(validate: [{ when: [BEFORE], where: { node: { id_EQ: "$jwt.sub" } } }]) @node { - id: ID! @id @unique + type Actor + @authorization(validate: [{ when: [BEFORE], where: { node: { id: { eq: "$jwt.sub" } } } }]) + @node { + id: ID! @id name: String - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Movie @node @authorization( - validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles_INCLUDES: "admin" } } }] + validate: [{ operations: [CREATE, UPDATE], where: { jwt: { roles: { includes: "admin" } } } }] ) { id: ID - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } @@ -80,7 +82,8 @@ describe("Batch Create, Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -88,14 +91,6 @@ describe("Batch Create, Auth", () => { create_this1.id = create_var0.id WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this2:HAS_WEBSITE]->(:Website) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var3 - } RETURN create_this1 } RETURN collect(create_this1 { .id }) AS data" @@ -144,7 +139,8 @@ describe("Batch Create, Auth", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -161,36 +157,21 @@ describe("Batch Create, Auth", () => { MERGE (create_this1)<-[create_this4:ACTED_IN]-(create_this3) SET create_this4.year = create_var2.edge.year - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:HAS_WEBSITE]->(:Website) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 + RETURN collect(NULL) AS create_var5 } WITH * WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $create_param3 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this8:HAS_WEBSITE]->(:Website) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var9 - } RETURN create_this1 } CALL { WITH create_this1 - MATCH (create_this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this11.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 + MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) + WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this7.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) + WITH DISTINCT create_this7 + WITH create_this7 { .name } AS create_this7 + RETURN collect(create_this7) AS create_var8 } - RETURN collect(create_this1 { .id, actors: create_var12 }) AS data" + RETURN collect(create_this1 { .id, actors: create_var8 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -242,295 +223,4 @@ describe("Batch Create, Auth", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const token = createBearerToken("secret", { sub: "1" }); - const result = await translateQuery(neoSchema, query, { token }); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND this3_actors_connect0_node.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - WITH * - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.roles IS NOT NULL AND $authorization_0_after_param2 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this4.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this11.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this18.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this25.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.sub IS NOT NULL AND create_this32.id = $jwt.sub)), \\"@neo4j/graphql/FORBIDDEN\\", [0]) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"isAuthenticated\\": true, - \\"jwt\\": { - \\"roles\\": [], - \\"sub\\": \\"1\\" - }, - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"authorization_0_after_param2\\": \\"admin\\", - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts index 9e1d895c35..70d805f127 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create-fields.test.ts @@ -27,11 +27,11 @@ describe("Batch Create, Scalar types", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Actor @node { - id: ID! @id @unique + id: ID! @id name: String born: Date createdAt: DateTime @timestamp(operations: [CREATE]) - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } @@ -40,7 +40,7 @@ describe("Batch Create, Scalar types", () => { runningTime: Duration location: Point createdAt: DateTime @timestamp(operations: [CREATE]) - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } @@ -77,7 +77,8 @@ describe("Batch Create, Scalar types", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -86,14 +87,6 @@ describe("Batch Create, Scalar types", () => { create_this1.id = create_var0.id, create_this1.runningTime = create_var0.runningTime, create_this1.location = point(create_var0.location) - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this2:HAS_WEBSITE]->(:Website) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var3 - } RETURN create_this1 } RETURN collect(create_this1 { .id }) AS data" @@ -161,7 +154,8 @@ describe("Batch Create, Scalar types", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -190,33 +184,17 @@ describe("Batch Create, Scalar types", () => { MERGE (create_this3)-[create_this7:HAS_WEBSITE]->(create_this6) RETURN collect(NULL) AS create_var8 } - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this9:HAS_WEBSITE]->(:Website) - WITH count(create_this9) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one\\", [0]) - RETURN c AS create_var10 - } - RETURN collect(NULL) AS create_var11 + RETURN collect(NULL) AS create_var9 } WITH create_this1, create_var0 CALL { WITH create_this1, create_var0 - UNWIND create_var0.website.create AS create_var12 - CREATE (create_this13:Website) + UNWIND create_var0.website.create AS create_var10 + CREATE (create_this11:Website) SET - create_this13.address = create_var12.node.address - MERGE (create_this1)-[create_this14:HAS_WEBSITE]->(create_this13) - RETURN collect(NULL) AS create_var15 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this16:HAS_WEBSITE]->(:Website) - WITH count(create_this16) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var17 + create_this11.address = create_var10.node.address + MERGE (create_this1)-[create_this12:HAS_WEBSITE]->(create_this11) + RETURN collect(NULL) AS create_var13 } RETURN create_this1 } @@ -240,11 +218,13 @@ describe("Batch Create, Scalar types", () => { \\"node\\": { \\"name\\": \\"actor 1\\", \\"website\\": { - \\"create\\": { - \\"node\\": { - \\"address\\": \\"Actor1.com\\" + \\"create\\": [ + { + \\"node\\": { + \\"address\\": \\"Actor1.com\\" + } } - } + ] } } } @@ -254,11 +234,13 @@ describe("Batch Create, Scalar types", () => { { \\"id\\": \\"2\\", \\"website\\": { - \\"create\\": { - \\"node\\": { - \\"address\\": \\"The Matrix2.com\\" + \\"create\\": [ + { + \\"node\\": { + \\"address\\": \\"The Matrix2.com\\" + } } - } + ] } } ] @@ -288,7 +270,8 @@ describe("Batch Create, Scalar types", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -307,33 +290,18 @@ describe("Batch Create, Scalar types", () => { MERGE (create_this1)<-[create_this4:ACTED_IN]-(create_this3) SET create_this4.year = create_var2.edge.year - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:HAS_WEBSITE]->(:Website) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this8:HAS_WEBSITE]->(:Website) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } CALL { WITH create_this1 - MATCH (create_this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 + MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) + WITH DISTINCT create_this7 + WITH create_this7 { .name } AS create_this7 + RETURN collect(create_this7) AS create_var8 } - RETURN collect(create_this1 { .id, actors: create_var12 }) AS data" + RETURN collect(create_this1 { .id, actors: create_var8 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -379,281 +347,4 @@ describe("Batch Create, Scalar types", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.createdAt = datetime() - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.createdAt = datetime() - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.createdAt = datetime() - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.createdAt = datetime() - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.createdAt = datetime() - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.createdAt = datetime() - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.createdAt = datetime() - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.createdAt = datetime(), - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/operations/batch/batch-create-interface.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create-interface.test.ts index 89224543fb..6d982fc1fb 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create-interface.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create-interface.test.ts @@ -34,20 +34,20 @@ describe("Batch Create, Interface", () => { type Actor implements Person @node { id: ID! name: String - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "EMPLOYED", direction: OUT, properties: "ActedIn") } type Modeler implements Person @node { id: ID! name: String - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "EMPLOYED", direction: OUT, properties: "ActedIn") } type Movie @node { id: ID - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) workers: [Person!]! @relationship(type: "EMPLOYED", direction: IN, properties: "ActedIn") } @@ -79,20 +79,13 @@ describe("Batch Create, Interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) SET create_this1.id = create_var0.id - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this2:HAS_WEBSITE]->(:Website) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var3 - } RETURN create_this1 } RETURN collect(create_this1 { .id }) AS data" @@ -144,7 +137,8 @@ describe("Batch Create, Interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.id = $this0_id WITH * @@ -153,22 +147,6 @@ describe("Batch Create, Interface", () => { SET this0_workersActor0_node.name = $this0_workersActor0_node_name MERGE (this0)<-[this0_workersActor0_relationship:EMPLOYED]-(this0_workersActor0_node) SET this0_workersActor0_relationship.year = $this0_workersActor0_relationship_year - WITH * - CALL { - WITH this0_workersActor0_node - MATCH (this0_workersActor0_node)-[this0_workersActor0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_workersActor0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_workersActor0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } RETURN this0 } CALL { @@ -180,22 +158,6 @@ describe("Batch Create, Interface", () => { SET this1_workersModeler0_node.name = $this1_workersModeler0_node_name MERGE (this1)<-[this1_workersModeler0_relationship:EMPLOYED]-(this1_workersModeler0_node) SET this1_workersModeler0_relationship.year = $this1_workersModeler0_relationship_year - WITH * - CALL { - WITH this1_workersModeler0_node - MATCH (this1_workersModeler0_node)-[this1_workersModeler0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_workersModeler0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDModeler.website must be less than or equal to one', [0]) - RETURN c AS this1_workersModeler0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } RETURN this1 } CALL { @@ -280,7 +242,7 @@ describe("Batch Create, Interface", () => { } } { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", workers: { connect: { where: { node: { id_EQ: "2" } } } } } + { id: "4", workers: { connect: { where: { node: { id: { eq: "2" } } } } } } ] ) { movies { @@ -299,7 +261,8 @@ describe("Batch Create, Interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.id = $this0_id WITH * @@ -308,22 +271,6 @@ describe("Batch Create, Interface", () => { SET this0_workersActor0_node.name = $this0_workersActor0_node_name MERGE (this0)<-[this0_workersActor0_relationship:EMPLOYED]-(this0_workersActor0_node) SET this0_workersActor0_relationship.year = $this0_workersActor0_relationship_year - WITH * - CALL { - WITH this0_workersActor0_node - MATCH (this0_workersActor0_node)-[this0_workersActor0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_workersActor0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_workersActor0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } RETURN this0 } CALL { @@ -335,22 +282,6 @@ describe("Batch Create, Interface", () => { SET this1_workersActor0_node.name = $this1_workersActor0_node_name MERGE (this1)<-[this1_workersActor0_relationship:EMPLOYED]-(this1_workersActor0_node) SET this1_workersActor0_relationship.year = $this1_workersActor0_relationship_year - WITH * - CALL { - WITH this1_workersActor0_node - MATCH (this1_workersActor0_node)-[this1_workersActor0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_workersActor0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_workersActor0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } RETURN this1 } CALL { @@ -360,14 +291,6 @@ describe("Batch Create, Interface", () => { CREATE (this2_website0_node:Website) SET this2_website0_node.address = $this2_website0_node_address MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } RETURN this2 } CALL { @@ -385,7 +308,7 @@ describe("Batch Create, Interface", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this3 UNWIND connectedNodes as this3_workers_connect0_node - MERGE (this3)<-[this3_workers_connect0_relationship:EMPLOYED]-(this3_workers_connect0_node) + CREATE (this3)<-[this3_workers_connect0_relationship:EMPLOYED]-(this3_workers_connect0_node) } } WITH this3, this3_workers_connect0_node @@ -402,20 +325,12 @@ describe("Batch Create, Interface", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this3 UNWIND connectedNodes as this3_workers_connect1_node - MERGE (this3)<-[this3_workers_connect1_relationship:EMPLOYED]-(this3_workers_connect1_node) + CREATE (this3)<-[this3_workers_connect1_relationship:EMPLOYED]-(this3_workers_connect1_node) } } WITH this3, this3_workers_connect1_node RETURN count(*) AS connect_this3_workers_connect_Modeler1 } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } RETURN this3 } CALL { @@ -423,8 +338,9 @@ describe("Batch Create, Interface", () => { CALL { WITH this0 MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) + WITH DISTINCT create_this1 WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 + RETURN collect(create_this1) AS create_var2 } CALL { WITH this0 @@ -449,8 +365,9 @@ describe("Batch Create, Interface", () => { CALL { WITH this1 MATCH (this1)-[create_this9:HAS_WEBSITE]->(create_this10:Website) + WITH DISTINCT create_this10 WITH create_this10 { .address } AS create_this10 - RETURN head(collect(create_this10)) AS create_var11 + RETURN collect(create_this10) AS create_var11 } CALL { WITH this1 @@ -475,8 +392,9 @@ describe("Batch Create, Interface", () => { CALL { WITH this2 MATCH (this2)-[create_this18:HAS_WEBSITE]->(create_this19:Website) + WITH DISTINCT create_this19 WITH create_this19 { .address } AS create_this19 - RETURN head(collect(create_this19)) AS create_var20 + RETURN collect(create_this19) AS create_var20 } CALL { WITH this2 @@ -501,8 +419,9 @@ describe("Batch Create, Interface", () => { CALL { WITH this3 MATCH (this3)-[create_this27:HAS_WEBSITE]->(create_this28:Website) + WITH DISTINCT create_this28 WITH create_this28 { .address } AS create_this28 - RETURN head(collect(create_this28)) AS create_var29 + RETURN collect(create_this28) AS create_var29 } CALL { WITH this3 diff --git a/packages/graphql/tests/tck/operations/batch/batch-create.test.ts b/packages/graphql/tests/tck/operations/batch/batch-create.test.ts index 0d1f7aa0c3..156649dccb 100644 --- a/packages/graphql/tests/tck/operations/batch/batch-create.test.ts +++ b/packages/graphql/tests/tck/operations/batch/batch-create.test.ts @@ -27,15 +27,15 @@ describe("Batch Create", () => { beforeAll(() => { typeDefs = /* GraphQL */ ` type Actor @node { - id: ID! @id @unique + id: ID! @id name: String - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") } type Movie @node { id: ID - website: Website @relationship(type: "HAS_WEBSITE", direction: OUT) + website: [Website!]! @relationship(type: "HAS_WEBSITE", direction: OUT) actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } @@ -67,20 +67,13 @@ describe("Batch Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) SET create_this1.id = create_var0.id - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this2:HAS_WEBSITE]->(:Website) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var3 - } RETURN create_this1 } RETURN collect(create_this1 { .id }) AS data" @@ -132,7 +125,8 @@ describe("Batch Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -159,33 +153,17 @@ describe("Batch Create", () => { MERGE (create_this3)-[create_this7:HAS_WEBSITE]->(create_this6) RETURN collect(NULL) AS create_var8 } - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this9:HAS_WEBSITE]->(:Website) - WITH count(create_this9) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one\\", [0]) - RETURN c AS create_var10 - } - RETURN collect(NULL) AS create_var11 + RETURN collect(NULL) AS create_var9 } WITH create_this1, create_var0 CALL { WITH create_this1, create_var0 - UNWIND create_var0.website.create AS create_var12 - CREATE (create_this13:Website) + UNWIND create_var0.website.create AS create_var10 + CREATE (create_this11:Website) SET - create_this13.address = create_var12.node.address - MERGE (create_this1)-[create_this14:HAS_WEBSITE]->(create_this13) - RETURN collect(NULL) AS create_var15 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this16:HAS_WEBSITE]->(:Website) - WITH count(create_this16) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var17 + create_this11.address = create_var10.node.address + MERGE (create_this1)-[create_this12:HAS_WEBSITE]->(create_this11) + RETURN collect(NULL) AS create_var13 } RETURN create_this1 } @@ -209,11 +187,13 @@ describe("Batch Create", () => { \\"node\\": { \\"name\\": \\"actor 1\\", \\"website\\": { - \\"create\\": { - \\"node\\": { - \\"address\\": \\"Actor1.com\\" + \\"create\\": [ + { + \\"node\\": { + \\"address\\": \\"Actor1.com\\" + } } - } + ] } } } @@ -223,11 +203,13 @@ describe("Batch Create", () => { { \\"id\\": \\"2\\", \\"website\\": { - \\"create\\": { - \\"node\\": { - \\"address\\": \\"The Matrix2.com\\" + \\"create\\": [ + { + \\"node\\": { + \\"address\\": \\"The Matrix2.com\\" + } } - } + ] } } ] @@ -257,7 +239,8 @@ describe("Batch Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -274,33 +257,18 @@ describe("Batch Create", () => { MERGE (create_this1)<-[create_this4:ACTED_IN]-(create_this3) SET create_this4.year = create_var2.edge.year - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:HAS_WEBSITE]->(:Website) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)-[create_this8:HAS_WEBSITE]->(:Website) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one\\", [0]) - RETURN c AS create_var9 + RETURN collect(NULL) AS create_var5 } RETURN create_this1 } CALL { WITH create_this1 - MATCH (create_this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 + MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) + WITH DISTINCT create_this7 + WITH create_this7 { .name } AS create_this7 + RETURN collect(create_this7) AS create_var8 } - RETURN collect(create_this1 { .id, actors: create_var12 }) AS data" + RETURN collect(create_this1 { .id, actors: create_var8 }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` @@ -352,8 +320,8 @@ describe("Batch Create", () => { mutation { createMovies( input: [ - { id: "1", actors: { connect: { where: { node: { id_EQ: "3" } } } } } - { id: "2", actors: { connect: { where: { node: { id_EQ: "4" } } } } } + { id: "1", actors: { connect: { where: { node: { id: { eq: "3" } } } } } } + { id: "2", actors: { connect: { where: { node: { id: { eq: "4" } } } } } } ] ) { movies { @@ -369,7 +337,8 @@ describe("Batch Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.id = $this0_id WITH * @@ -384,20 +353,12 @@ describe("Batch Create", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actors_connect0_node - MERGE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) + CREATE (this0)<-[this0_actors_connect0_relationship:ACTED_IN]-(this0_actors_connect0_node) } } WITH this0, this0_actors_connect0_node RETURN count(*) AS connect_this0_actors_connect_Actor0 } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } RETURN this0 } CALL { @@ -415,20 +376,12 @@ describe("Batch Create", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this1 UNWIND connectedNodes as this1_actors_connect0_node - MERGE (this1)<-[this1_actors_connect0_relationship:ACTED_IN]-(this1_actors_connect0_node) + CREATE (this1)<-[this1_actors_connect0_relationship:ACTED_IN]-(this1_actors_connect0_node) } } WITH this1, this1_actors_connect0_node RETURN count(*) AS connect_this1_actors_connect_Actor0 } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } RETURN this1 } CALL { @@ -436,6 +389,7 @@ describe("Batch Create", () => { CALL { WITH this0 MATCH (this0)<-[create_this0:ACTED_IN]-(create_this1:Actor) + WITH DISTINCT create_this1 WITH create_this1 { .name } AS create_this1 RETURN collect(create_this1) AS create_var2 } @@ -446,6 +400,7 @@ describe("Batch Create", () => { CALL { WITH this1 MATCH (this1)<-[create_this4:ACTED_IN]-(create_this5:Actor) + WITH DISTINCT create_this5 WITH create_this5 { .name } AS create_this5 RETURN collect(create_this5) AS create_var6 } @@ -464,273 +419,4 @@ describe("Batch Create", () => { }" `); }); - - test("heterogeneous batch", async () => { - const query = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { id: "1", actors: { create: [{ node: { name: "actor 1" }, edge: { year: 2022 } }] } } - { id: "2", actors: { create: [{ node: { name: "actor 2" }, edge: { year: 1999 } }] } } - { id: "3", website: { create: { node: { address: "mywebsite.com" } } } } - { id: "4", actors: { connect: { where: { node: { id_EQ: "2" } } } } } - { - id: "5" - actors: { - connectOrCreate: { - where: { node: { id_EQ: "2" } } - onCreate: { node: { name: "actor 2" } } - } - } - } - ] - ) { - movies { - id - website { - address - } - actors { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CREATE (this0_actors0_node:Actor) - SET this0_actors0_node.id = randomUUID() - SET this0_actors0_node.name = $this0_actors0_node_name - MERGE (this0)<-[this0_actors0_relationship:ACTED_IN]-(this0_actors0_node) - SET this0_actors0_relationship.year = $this0_actors0_relationship_year - WITH * - CALL { - WITH this0_actors0_node - MATCH (this0_actors0_node)-[this0_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this0_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this0 - MATCH (this0)-[this0_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this0_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this0_website_Website_unique_ignored - } - RETURN this0 - } - CALL { - CREATE (this1:Movie) - SET this1.id = $this1_id - WITH * - CREATE (this1_actors0_node:Actor) - SET this1_actors0_node.id = randomUUID() - SET this1_actors0_node.name = $this1_actors0_node_name - MERGE (this1)<-[this1_actors0_relationship:ACTED_IN]-(this1_actors0_node) - SET this1_actors0_relationship.year = $this1_actors0_relationship_year - WITH * - CALL { - WITH this1_actors0_node - MATCH (this1_actors0_node)-[this1_actors0_node_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_actors0_node_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDActor.website must be less than or equal to one', [0]) - RETURN c AS this1_actors0_node_website_Website_unique_ignored - } - WITH * - CALL { - WITH this1 - MATCH (this1)-[this1_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this1_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this1_website_Website_unique_ignored - } - RETURN this1 - } - CALL { - CREATE (this2:Movie) - SET this2.id = $this2_id - WITH * - CREATE (this2_website0_node:Website) - SET this2_website0_node.address = $this2_website0_node_address - MERGE (this2)-[:HAS_WEBSITE]->(this2_website0_node) - WITH * - CALL { - WITH this2 - MATCH (this2)-[this2_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this2_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this2_website_Website_unique_ignored - } - RETURN this2 - } - CALL { - CREATE (this3:Movie) - SET this3.id = $this3_id - WITH * - CALL { - WITH this3 - OPTIONAL MATCH (this3_actors_connect0_node:Actor) - WHERE this3_actors_connect0_node.id = $this3_actors_connect0_node_param0 - CALL { - WITH * - WITH collect(this3_actors_connect0_node) as connectedNodes, collect(this3) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this3 - UNWIND connectedNodes as this3_actors_connect0_node - MERGE (this3)<-[this3_actors_connect0_relationship:ACTED_IN]-(this3_actors_connect0_node) - } - } - WITH this3, this3_actors_connect0_node - RETURN count(*) AS connect_this3_actors_connect_Actor0 - } - WITH * - CALL { - WITH this3 - MATCH (this3)-[this3_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this3_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this3_website_Website_unique_ignored - } - RETURN this3 - } - CALL { - CREATE (this4:Movie) - SET this4.id = $this4_id - WITH this4 - CALL { - WITH this4 - MERGE (this4_actors_connectOrCreate0:Actor { id: $this4_actors_connectOrCreate_param0 }) - ON CREATE SET - this4_actors_connectOrCreate0.name = $this4_actors_connectOrCreate_param1 - MERGE (this4)<-[this4_actors_connectOrCreate_this0:ACTED_IN]-(this4_actors_connectOrCreate0) - RETURN count(*) AS _ - } - WITH * - CALL { - WITH this4 - MATCH (this4)-[this4_website_Website_unique:HAS_WEBSITE]->(:Website) - WITH count(this4_website_Website_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.website must be less than or equal to one', [0]) - RETURN c AS this4_website_Website_unique_ignored - } - RETURN this4 - } - CALL { - WITH this0 - CALL { - WITH this0 - MATCH (this0)-[create_this0:HAS_WEBSITE]->(create_this1:Website) - WITH create_this1 { .address } AS create_this1 - RETURN head(collect(create_this1)) AS create_var2 - } - CALL { - WITH this0 - MATCH (this0)<-[create_this3:ACTED_IN]-(create_this4:Actor) - WITH create_this4 { .name } AS create_this4 - RETURN collect(create_this4) AS create_var5 - } - RETURN this0 { .id, website: create_var2, actors: create_var5 } AS create_var6 - } - CALL { - WITH this1 - CALL { - WITH this1 - MATCH (this1)-[create_this7:HAS_WEBSITE]->(create_this8:Website) - WITH create_this8 { .address } AS create_this8 - RETURN head(collect(create_this8)) AS create_var9 - } - CALL { - WITH this1 - MATCH (this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) - WITH create_this11 { .name } AS create_this11 - RETURN collect(create_this11) AS create_var12 - } - RETURN this1 { .id, website: create_var9, actors: create_var12 } AS create_var13 - } - CALL { - WITH this2 - CALL { - WITH this2 - MATCH (this2)-[create_this14:HAS_WEBSITE]->(create_this15:Website) - WITH create_this15 { .address } AS create_this15 - RETURN head(collect(create_this15)) AS create_var16 - } - CALL { - WITH this2 - MATCH (this2)<-[create_this17:ACTED_IN]-(create_this18:Actor) - WITH create_this18 { .name } AS create_this18 - RETURN collect(create_this18) AS create_var19 - } - RETURN this2 { .id, website: create_var16, actors: create_var19 } AS create_var20 - } - CALL { - WITH this3 - CALL { - WITH this3 - MATCH (this3)-[create_this21:HAS_WEBSITE]->(create_this22:Website) - WITH create_this22 { .address } AS create_this22 - RETURN head(collect(create_this22)) AS create_var23 - } - CALL { - WITH this3 - MATCH (this3)<-[create_this24:ACTED_IN]-(create_this25:Actor) - WITH create_this25 { .name } AS create_this25 - RETURN collect(create_this25) AS create_var26 - } - RETURN this3 { .id, website: create_var23, actors: create_var26 } AS create_var27 - } - CALL { - WITH this4 - CALL { - WITH this4 - MATCH (this4)-[create_this28:HAS_WEBSITE]->(create_this29:Website) - WITH create_this29 { .address } AS create_this29 - RETURN head(collect(create_this29)) AS create_var30 - } - CALL { - WITH this4 - MATCH (this4)<-[create_this31:ACTED_IN]-(create_this32:Actor) - WITH create_this32 { .name } AS create_this32 - RETURN collect(create_this32) AS create_var33 - } - RETURN this4 { .id, website: create_var30, actors: create_var33 } AS create_var34 - } - RETURN [create_var6, create_var13, create_var20, create_var27, create_var34] AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"1\\", - \\"this0_actors0_node_name\\": \\"actor 1\\", - \\"this0_actors0_relationship_year\\": { - \\"low\\": 2022, - \\"high\\": 0 - }, - \\"this1_id\\": \\"2\\", - \\"this1_actors0_node_name\\": \\"actor 2\\", - \\"this1_actors0_relationship_year\\": { - \\"low\\": 1999, - \\"high\\": 0 - }, - \\"this2_id\\": \\"3\\", - \\"this2_website0_node_address\\": \\"mywebsite.com\\", - \\"this3_id\\": \\"4\\", - \\"this3_actors_connect0_node_param0\\": \\"2\\", - \\"this4_id\\": \\"5\\", - \\"this4_actors_connectOrCreate_param0\\": \\"2\\", - \\"this4_actors_connectOrCreate_param1\\": \\"actor 2\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); }); diff --git a/packages/graphql/tests/tck/operations/connect.test.ts b/packages/graphql/tests/tck/operations/connect.test.ts index abf44f13ed..ff699330b5 100644 --- a/packages/graphql/tests/tck/operations/connect.test.ts +++ b/packages/graphql/tests/tck/operations/connect.test.ts @@ -49,7 +49,7 @@ describe("Cypher Connect", () => { id: ID! description: String! url: String! - color: Color! @relationship(type: "OF_COLOR", direction: OUT) + color: [Color!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -69,12 +69,12 @@ describe("Cypher Connect", () => { colors: { connect: [ { - where: { node: { name_EQ: "Red" } } + where: { node: { name: { eq: "Red" } } } connect: { photos: [ { - where: { node: { id_EQ: "123" } } - connect: { color: { where: { node: { id_EQ: "134" } } } } + where: { node: { id: { eq: "123" } } } + connect: { color: { where: { node: { id: { eq: "134" } } } } } } ] } @@ -84,12 +84,12 @@ describe("Cypher Connect", () => { photos: { connect: [ { - where: { node: { id_EQ: "321" } } - connect: { color: { where: { node: { name_EQ: "Green" } } } } + where: { node: { id: { eq: "321" } } } + connect: { color: { where: { node: { name: { eq: "Green" } } } } } } { - where: { node: { id_EQ: "33211" } } - connect: { color: { where: { node: { name_EQ: "Red" } } } } + where: { node: { id: { eq: "33211" } } } + connect: { color: { where: { node: { name: { eq: "Red" } } } } } } ] } @@ -106,7 +106,8 @@ describe("Cypher Connect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Product) SET this0.id = $this0_id SET this0.name = $this0_name @@ -122,7 +123,7 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_colors_connect0_node - MERGE (this0)-[:HAS_COLOR]->(this0_colors_connect0_node) + CREATE (this0)-[:HAS_COLOR]->(this0_colors_connect0_node) } } WITH this0, this0_colors_connect0_node @@ -137,17 +138,9 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_colors_connect0_node UNWIND connectedNodes as this0_colors_connect0_node_photos0_node - MERGE (this0_colors_connect0_node)<-[:OF_COLOR]-(this0_colors_connect0_node_photos0_node) + CREATE (this0_colors_connect0_node)<-[:OF_COLOR]-(this0_colors_connect0_node_photos0_node) } } - WITH this0, this0_colors_connect0_node, this0_colors_connect0_node_photos0_node - CALL { - WITH this0_colors_connect0_node_photos0_node - MATCH (this0_colors_connect0_node_photos0_node)-[this0_colors_connect0_node_photos0_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_colors_connect0_node_photos0_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_colors_connect0_node_photos0_node_color_Color_unique_ignored - } WITH this0, this0_colors_connect0_node, this0_colors_connect0_node_photos0_node CALL { WITH this0, this0_colors_connect0_node, this0_colors_connect0_node_photos0_node @@ -160,17 +153,9 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_colors_connect0_node_photos0_node UNWIND connectedNodes as this0_colors_connect0_node_photos0_node_color0_node - MERGE (this0_colors_connect0_node_photos0_node)-[:OF_COLOR]->(this0_colors_connect0_node_photos0_node_color0_node) + CREATE (this0_colors_connect0_node_photos0_node)-[:OF_COLOR]->(this0_colors_connect0_node_photos0_node_color0_node) } } - WITH this0, this0_colors_connect0_node, this0_colors_connect0_node_photos0_node, this0_colors_connect0_node_photos0_node_color0_node - CALL { - WITH this0_colors_connect0_node_photos0_node - MATCH (this0_colors_connect0_node_photos0_node)-[this0_colors_connect0_node_photos0_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_colors_connect0_node_photos0_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_colors_connect0_node_photos0_node_color_Color_unique_ignored - } WITH this0, this0_colors_connect0_node, this0_colors_connect0_node_photos0_node, this0_colors_connect0_node_photos0_node_color0_node RETURN count(*) AS connect_this0_colors_connect0_node_photos0_node_color_Color0 } @@ -190,7 +175,7 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_photos_connect0_node - MERGE (this0)-[:HAS_PHOTO]->(this0_photos_connect0_node) + CREATE (this0)-[:HAS_PHOTO]->(this0_photos_connect0_node) } } WITH this0, this0_photos_connect0_node @@ -205,17 +190,9 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_photos_connect0_node UNWIND connectedNodes as this0_photos_connect0_node_color0_node - MERGE (this0_photos_connect0_node)-[:OF_COLOR]->(this0_photos_connect0_node_color0_node) + CREATE (this0_photos_connect0_node)-[:OF_COLOR]->(this0_photos_connect0_node_color0_node) } } - WITH this0, this0_photos_connect0_node, this0_photos_connect0_node_color0_node - CALL { - WITH this0_photos_connect0_node - MATCH (this0_photos_connect0_node)-[this0_photos_connect0_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_photos_connect0_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_photos_connect0_node_color_Color_unique_ignored - } WITH this0, this0_photos_connect0_node, this0_photos_connect0_node_color0_node RETURN count(*) AS connect_this0_photos_connect0_node_color_Color0 } @@ -233,7 +210,7 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_photos_connect1_node - MERGE (this0)-[:HAS_PHOTO]->(this0_photos_connect1_node) + CREATE (this0)-[:HAS_PHOTO]->(this0_photos_connect1_node) } } WITH this0, this0_photos_connect1_node @@ -248,17 +225,9 @@ describe("Cypher Connect", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_photos_connect1_node UNWIND connectedNodes as this0_photos_connect1_node_color0_node - MERGE (this0_photos_connect1_node)-[:OF_COLOR]->(this0_photos_connect1_node_color0_node) + CREATE (this0_photos_connect1_node)-[:OF_COLOR]->(this0_photos_connect1_node_color0_node) } } - WITH this0, this0_photos_connect1_node, this0_photos_connect1_node_color0_node - CALL { - WITH this0_photos_connect1_node - MATCH (this0_photos_connect1_node)-[this0_photos_connect1_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_photos_connect1_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_photos_connect1_node_color_Color_unique_ignored - } WITH this0, this0_photos_connect1_node, this0_photos_connect1_node_color0_node RETURN count(*) AS connect_this0_photos_connect1_node_color_Color0 } diff --git a/packages/graphql/tests/tck/operations/create.test.ts b/packages/graphql/tests/tck/operations/create.test.ts index 118243b809..75c85ab96e 100644 --- a/packages/graphql/tests/tck/operations/create.test.ts +++ b/packages/graphql/tests/tck/operations/create.test.ts @@ -56,7 +56,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -92,7 +93,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -136,7 +138,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -218,7 +221,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -302,7 +306,7 @@ describe("Cypher Create", () => { test("Simple create and connect", async () => { const query = /* GraphQL */ ` mutation { - createMovies(input: [{ id: 1, actors: { connect: [{ where: { node: { name_EQ: "Dan" } } }] } }]) { + createMovies(input: [{ id: 1, actors: { connect: [{ where: { node: { name: { eq: "Dan" } } } }] } }]) { movies { id } @@ -313,7 +317,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.id = $this0_id WITH * @@ -328,7 +333,7 @@ describe("Cypher Create", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_actors_connect0_node - MERGE (this0)<-[:ACTED_IN]-(this0_actors_connect0_node) + CREATE (this0)<-[:ACTED_IN]-(this0_actors_connect0_node) } } WITH this0, this0_actors_connect0_node @@ -355,11 +360,11 @@ describe("Cypher Create", () => { test("Simple create -> relationship field -> connection(where)", async () => { const query = /* GraphQL */ ` mutation { - createActors(input: { name: "Dan", movies: { connect: { where: { node: { id_EQ: 1 } } } } }) { + createActors(input: { name: "Dan", movies: { connect: { where: { node: { id: { eq: 1 } } } } } }) { actors { name movies { - actorsConnection(where: { node: { name_EQ: "Dan" } }) { + actorsConnection(where: { node: { name: { eq: "Dan" } } }) { totalCount edges { node { @@ -376,7 +381,8 @@ describe("Cypher Create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Actor) SET this0.name = $this0_name WITH * @@ -391,7 +397,7 @@ describe("Cypher Create", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_movies_connect0_node - MERGE (this0)-[:ACTED_IN]->(this0_movies_connect0_node) + CREATE (this0)-[:ACTED_IN]->(this0_movies_connect0_node) } } WITH this0, this0_movies_connect0_node @@ -404,6 +410,7 @@ describe("Cypher Create", () => { CALL { WITH this0 MATCH (this0)-[create_this0:ACTED_IN]->(create_this1:Movie) + WITH DISTINCT create_this1 CALL { WITH create_this1 MATCH (create_this1)<-[create_this2:ACTED_IN]-(create_this3:Actor) diff --git a/packages/graphql/tests/tck/operations/delete-interface.test.ts b/packages/graphql/tests/tck/operations/delete-interface.test.ts index acc1344a01..94cd3be65a 100644 --- a/packages/graphql/tests/tck/operations/delete-interface.test.ts +++ b/packages/graphql/tests/tck/operations/delete-interface.test.ts @@ -28,7 +28,7 @@ describe("Cypher Delete - interface", () => { typeDefs = /* GraphQL */ ` type Episode @node { runtime: Int! - series: Series! @relationship(type: "HAS_EPISODE", direction: IN) + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) } interface Production { @@ -80,7 +80,7 @@ describe("Cypher Delete - interface", () => { test("Simple Delete", async () => { const query = /* GraphQL */ ` mutation { - deleteActors(where: { name_EQ: "Keanu" }) { + deleteActors(where: { name: { eq: "Keanu" } }) { nodesDeleted } } @@ -89,7 +89,8 @@ describe("Cypher Delete - interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 DETACH DELETE this" `); @@ -104,7 +105,10 @@ describe("Cypher Delete - interface", () => { test("Single Nested Delete", async () => { const query = /* GraphQL */ ` mutation { - deleteActors(where: { name_EQ: "Keanu" }, delete: { actedIn: { where: { node: { title_EQ: "Matrix" } } } }) { + deleteActors( + where: { name: { eq: "Keanu" } } + delete: { actedIn: { where: { node: { title: { eq: "Matrix" } } } } } + ) { nodesDeleted } } @@ -113,7 +117,8 @@ describe("Cypher Delete - interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -155,8 +160,8 @@ describe("Cypher Delete - interface", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } - delete: { actedIn: { where: { node: { typename_IN: [Movie], title_EQ: "Matrix" } } } } + where: { name: { eq: "Keanu" } } + delete: { actedIn: { where: { node: { typename: [Movie], title: { eq: "Matrix" } } } } } ) { nodesDeleted } @@ -166,7 +171,8 @@ describe("Cypher Delete - interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -208,9 +214,11 @@ describe("Cypher Delete - interface", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } + where: { name: { eq: "Keanu" } } delete: { - actedIn: { where: { node: { OR: [{ title_EQ: "Matrix" }, { title_EQ: "Matrix Reloaded" }] } } } + actedIn: { + where: { node: { OR: [{ title: { eq: "Matrix" } }, { title: { eq: "Matrix Reloaded" } }] } } + } } ) { nodesDeleted @@ -221,7 +229,8 @@ describe("Cypher Delete - interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -265,11 +274,11 @@ describe("Cypher Delete - interface", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } + where: { name: { eq: "Keanu" } } delete: { actedIn: { - where: { node: { title_EQ: "Matrix" } } - delete: { actors: { where: { node: { name_EQ: "Gloria Foster" } } } } + where: { node: { title: { eq: "Matrix" } } } + delete: { actors: { where: { node: { name: { eq: "Gloria Foster" } } } } } } } ) { @@ -281,7 +290,8 @@ describe("Cypher Delete - interface", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/operations/delete-union.test.ts b/packages/graphql/tests/tck/operations/delete-union.test.ts index 6110f385b1..8fc9db3399 100644 --- a/packages/graphql/tests/tck/operations/delete-union.test.ts +++ b/packages/graphql/tests/tck/operations/delete-union.test.ts @@ -28,7 +28,7 @@ describe("Cypher Delete - union", () => { typeDefs = /* GraphQL */ ` type Episode @node { runtime: Int! - series: Series! @relationship(type: "HAS_EPISODE", direction: IN) + series: [Series!]! @relationship(type: "HAS_EPISODE", direction: IN) } union Production = Movie | Series @@ -75,7 +75,7 @@ describe("Cypher Delete - union", () => { test("Simple Delete", async () => { const query = /* GraphQL */ ` mutation { - deleteActors(where: { name_EQ: "Keanu" }) { + deleteActors(where: { name: { eq: "Keanu" } }) { nodesDeleted } } @@ -84,7 +84,8 @@ describe("Cypher Delete - union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 DETACH DELETE this" `); @@ -100,8 +101,8 @@ describe("Cypher Delete - union", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } - delete: { actedIn: { Movie: { where: { node: { title_EQ: "Matrix" } } } } } + where: { name: { eq: "Keanu" } } + delete: { actedIn: { Movie: { where: { node: { title: { eq: "Matrix" } } } } } } ) { nodesDeleted } @@ -111,7 +112,8 @@ describe("Cypher Delete - union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -141,12 +143,12 @@ describe("Cypher Delete - union", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } + where: { name: { eq: "Keanu" } } delete: { actedIn: { Movie: [ - { where: { node: { title_EQ: "Matrix" } } } - { where: { node: { title_EQ: "Matrix Reloaded" } } } + { where: { node: { title: { eq: "Matrix" } } } } + { where: { node: { title: { eq: "Matrix Reloaded" } } } } ] } } @@ -159,7 +161,8 @@ describe("Cypher Delete - union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -201,12 +204,12 @@ describe("Cypher Delete - union", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } + where: { name: { eq: "Keanu" } } delete: { actedIn: { Movie: { - where: { node: { title_EQ: "Matrix" } } - delete: { actors: { where: { node: { name_EQ: "Gloria Foster" } } } } + where: { node: { title: { eq: "Matrix" } } } + delete: { actors: { where: { node: { name: { eq: "Gloria Foster" } } } } } } } } @@ -219,7 +222,8 @@ describe("Cypher Delete - union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { @@ -262,12 +266,14 @@ describe("Cypher Delete - union", () => { const query = /* GraphQL */ ` mutation { deleteActors( - where: { name_EQ: "Keanu" } + where: { name: { eq: "Keanu" } } delete: { actedIn: { Movie: { - where: { node: { title_EQ: "Matrix" } } - delete: { workers: { ScreenWriter: { where: { node: { name_EQ: "Wachowski" } } } } } + where: { node: { title: { eq: "Matrix" } } } + delete: { + workers: { ScreenWriter: { where: { node: { name: { eq: "Wachowski" } } } } } + } } } } @@ -280,7 +286,8 @@ describe("Cypher Delete - union", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/operations/delete.test.ts b/packages/graphql/tests/tck/operations/delete.test.ts index 0ca51b5d22..86910e1a52 100644 --- a/packages/graphql/tests/tck/operations/delete.test.ts +++ b/packages/graphql/tests/tck/operations/delete.test.ts @@ -46,7 +46,7 @@ describe("Cypher Delete", () => { test("Simple Delete", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: "123" }) { + deleteMovies(where: { id: { eq: "123" } }) { nodesDeleted } } @@ -55,7 +55,8 @@ describe("Cypher Delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 DETACH DELETE this" `); @@ -70,7 +71,10 @@ describe("Cypher Delete", () => { test("Single Nested Delete", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: 123 }, delete: { actors: { where: { node: { name_EQ: "Actor to delete" } } } }) { + deleteMovies( + where: { id: { eq: 123 } } + delete: { actors: { where: { node: { name: { eq: "Actor to delete" } } } } } + ) { nodesDeleted } } @@ -79,7 +83,8 @@ describe("Cypher Delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -109,11 +114,11 @@ describe("Cypher Delete", () => { const query = /* GraphQL */ ` mutation { deleteMovies( - where: { id_EQ: 123 } + where: { id: { eq: 123 } } delete: { actors: [ - { where: { node: { name_EQ: "Actor to delete" } } } - { where: { node: { name_EQ: "Another actor to delete" } } } + { where: { node: { name: { eq: "Actor to delete" } } } } + { where: { node: { name: { eq: "Another actor to delete" } } } } ] } ) { @@ -125,7 +130,8 @@ describe("Cypher Delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -167,11 +173,11 @@ describe("Cypher Delete", () => { const query = /* GraphQL */ ` mutation { deleteMovies( - where: { id_EQ: 123 } + where: { id: { eq: 123 } } delete: { actors: { - where: { node: { name_EQ: "Actor to delete" } } - delete: { movies: { where: { node: { id_EQ: 321 } } } } + where: { node: { name: { eq: "Actor to delete" } } } + delete: { movies: { where: { node: { id: { eq: 321 } } } } } } } ) { @@ -183,7 +189,8 @@ describe("Cypher Delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -226,14 +233,14 @@ describe("Cypher Delete", () => { const query = /* GraphQL */ ` mutation { deleteMovies( - where: { id_EQ: 123 } + where: { id: { eq: 123 } } delete: { actors: { - where: { node: { name_EQ: "Actor to delete" } } + where: { node: { name: { eq: "Actor to delete" } } } delete: { movies: { - where: { node: { id_EQ: 321 } } - delete: { actors: { where: { node: { name_EQ: "Another actor to delete" } } } } + where: { node: { id: { eq: 321 } } } + delete: { actors: { where: { node: { name: { eq: "Another actor to delete" } } } } } } } } @@ -247,7 +254,8 @@ describe("Cypher Delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/operations/disconnect.test.ts b/packages/graphql/tests/tck/operations/disconnect.test.ts index 3ee1eb25bb..fd2c557f14 100644 --- a/packages/graphql/tests/tck/operations/disconnect.test.ts +++ b/packages/graphql/tests/tck/operations/disconnect.test.ts @@ -49,7 +49,7 @@ describe("Cypher Disconnect", () => { id: ID! description: String! url: String! - color: Color! @relationship(type: "OF_COLOR", direction: OUT) + color: [Color!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -68,12 +68,12 @@ describe("Cypher Disconnect", () => { colors: { disconnect: [ { - where: { node: { name_EQ: "Red" } } + where: { node: { name: { eq: "Red" } } } disconnect: { photos: [ { - where: { node: { id_EQ: "123" } } - disconnect: { color: { where: { node: { id_EQ: "134" } } } } + where: { node: { id: { eq: "123" } } } + disconnect: { color: { where: { node: { id: { eq: "134" } } } } } } ] } @@ -83,12 +83,12 @@ describe("Cypher Disconnect", () => { photos: { disconnect: [ { - where: { node: { id_EQ: "321" } } - disconnect: { color: { where: { node: { name_EQ: "Green" } } } } + where: { node: { id: { eq: "321" } } } + disconnect: { color: { where: { node: { name: { eq: "Green" } } } } } } { - where: { node: { id_EQ: "33211" } } - disconnect: { color: { where: { node: { name_EQ: "Red" } } } } + where: { node: { id: { eq: "33211" } } } + disconnect: { color: { where: { node: { name: { eq: "Red" } } } } } } ] } @@ -104,7 +104,8 @@ describe("Cypher Disconnect", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Product) + "CYPHER 5 + MATCH (this:Product) SET this.id = $this_update_id_SET SET this.name = $this_update_name_SET WITH this @@ -131,7 +132,7 @@ describe("Cypher Disconnect", () => { CALL { WITH this, this_colors0_disconnect0, this_colors0_disconnect0_photos0 OPTIONAL MATCH (this_colors0_disconnect0_photos0)-[this_colors0_disconnect0_photos0_color0_rel:OF_COLOR]->(this_colors0_disconnect0_photos0_color0:Color) - WHERE this_colors0_disconnect0_photos0_color0.id = $updateProducts_args_update_colors0_disconnect0_disconnect_photos_disconnect_color_where_Color_this_colors0_disconnect0_photos0_color0param0 + WHERE this_colors0_disconnect0_photos0_color0.id = $updateProducts_args_update_colors0_disconnect0_disconnect_photos0_disconnect_color0_where_Color_this_colors0_disconnect0_photos0_color0param0 CALL { WITH this_colors0_disconnect0_photos0_color0, this_colors0_disconnect0_photos0_color0_rel, this_colors0_disconnect0_photos0 WITH collect(this_colors0_disconnect0_photos0_color0) as this_colors0_disconnect0_photos0_color0, this_colors0_disconnect0_photos0_color0_rel, this_colors0_disconnect0_photos0 @@ -158,7 +159,7 @@ describe("Cypher Disconnect", () => { CALL { WITH this, this_photos0_disconnect0 OPTIONAL MATCH (this_photos0_disconnect0)-[this_photos0_disconnect0_color0_rel:OF_COLOR]->(this_photos0_disconnect0_color0:Color) - WHERE this_photos0_disconnect0_color0.name = $updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect0_color0param0 + WHERE this_photos0_disconnect0_color0.name = $updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect0_color0param0 CALL { WITH this_photos0_disconnect0_color0, this_photos0_disconnect0_color0_rel, this_photos0_disconnect0 WITH collect(this_photos0_disconnect0_color0) as this_photos0_disconnect0_color0, this_photos0_disconnect0_color0_rel, this_photos0_disconnect0 @@ -183,7 +184,7 @@ describe("Cypher Disconnect", () => { CALL { WITH this, this_photos0_disconnect1 OPTIONAL MATCH (this_photos0_disconnect1)-[this_photos0_disconnect1_color0_rel:OF_COLOR]->(this_photos0_disconnect1_color0:Color) - WHERE this_photos0_disconnect1_color0.name = $updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect1_color0param0 + WHERE this_photos0_disconnect1_color0.name = $updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect1_color0param0 CALL { WITH this_photos0_disconnect1_color0, this_photos0_disconnect1_color0_rel, this_photos0_disconnect1 WITH collect(this_photos0_disconnect1_color0) as this_photos0_disconnect1_color0, this_photos0_disconnect1_color0_rel, this_photos0_disconnect1 @@ -203,11 +204,11 @@ describe("Cypher Disconnect", () => { \\"this_update_name_SET\\": \\"Nested Connect\\", \\"updateProducts_args_update_colors0_disconnect0_where_Color_this_colors0_disconnect0param0\\": \\"Red\\", \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos0_where_Photo_this_colors0_disconnect0_photos0param0\\": \\"123\\", - \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos_disconnect_color_where_Color_this_colors0_disconnect0_photos0_color0param0\\": \\"134\\", + \\"updateProducts_args_update_colors0_disconnect0_disconnect_photos0_disconnect_color0_where_Color_this_colors0_disconnect0_photos0_color0param0\\": \\"134\\", \\"updateProducts_args_update_photos0_disconnect0_where_Photo_this_photos0_disconnect0param0\\": \\"321\\", - \\"updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect0_color0param0\\": \\"Green\\", + \\"updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect0_color0param0\\": \\"Green\\", \\"updateProducts_args_update_photos0_disconnect1_where_Photo_this_photos0_disconnect1param0\\": \\"33211\\", - \\"updateProducts_args_update_photos0_disconnect_disconnect_color_where_Color_this_photos0_disconnect1_color0param0\\": \\"Red\\", + \\"updateProducts_args_update_photos0_disconnect0_disconnect_color0_where_Color_this_photos0_disconnect1_color0param0\\": \\"Red\\", \\"updateProducts\\": { \\"args\\": { \\"update\\": { @@ -219,7 +220,9 @@ describe("Cypher Disconnect", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Red\\" + \\"name\\": { + \\"eq\\": \\"Red\\" + } } }, \\"disconnect\\": { @@ -227,17 +230,23 @@ describe("Cypher Disconnect", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"123\\" + \\"id\\": { + \\"eq\\": \\"123\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"134\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"id\\": { + \\"eq\\": \\"134\\" + } + } } } - } + ] } } ] @@ -252,33 +261,45 @@ describe("Cypher Disconnect", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"321\\" + \\"id\\": { + \\"eq\\": \\"321\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Green\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Green\\" + } + } } } - } + ] } }, { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"33211\\" + \\"id\\": { + \\"eq\\": \\"33211\\" + } } }, \\"disconnect\\": { - \\"color\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Red\\" + \\"color\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Red\\" + } + } } } - } + ] } } ] diff --git a/packages/graphql/tests/tck/operations/update.test.ts b/packages/graphql/tests/tck/operations/update.test.ts index ac9a1a2ab2..030476b972 100644 --- a/packages/graphql/tests/tck/operations/update.test.ts +++ b/packages/graphql/tests/tck/operations/update.test.ts @@ -50,7 +50,7 @@ describe("Cypher Update", () => { test("Simple Update", async () => { const query = /* GraphQL */ ` mutation { - updateMovies(where: { id_EQ: "1" }, update: { id_SET: "2" }) { + updateMovies(where: { id: { eq: "1" } }, update: { id_SET: "2" }) { movies { id } @@ -61,7 +61,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -80,10 +81,13 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: [ - { where: { node: { name_EQ: "old name" } }, update: { node: { name_SET: "new name" } } } + { + where: { node: { name: { eq: "old name" } } } + update: { node: { name_SET: "new name" } } + } ] } ) { @@ -97,7 +101,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH this CALL { @@ -122,7 +127,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"old name\\" + \\"name\\": { + \\"eq\\": \\"old name\\" + } } }, \\"update\\": { @@ -144,17 +151,17 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: [ { - where: { node: { name_EQ: "old actor name" } } + where: { node: { name: { eq: "old actor name" } } } update: { node: { name_SET: "new actor name" movies: [ { - where: { node: { id_EQ: "old movie title" } } + where: { node: { id: { eq: "old movie title" } } } update: { node: { title_SET: "new movie title" } } } ] @@ -174,7 +181,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH this CALL { @@ -209,7 +217,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"old actor name\\" + \\"name\\": { + \\"eq\\": \\"old actor name\\" + } } }, \\"update\\": { @@ -219,7 +229,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"old movie title\\" + \\"id\\": { + \\"eq\\": \\"old movie title\\" + } } }, \\"update\\": { @@ -245,8 +257,8 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } - update: { actors: { connect: [{ where: { node: { name_EQ: "Daniel" } } }] } } + where: { id: { eq: "1" } } + update: { actors: { connect: [{ where: { node: { name: { eq: "Daniel" } } } }] } } ) { movies { id @@ -258,7 +270,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -272,7 +285,7 @@ describe("Cypher Update", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) + CREATE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) } } WITH this, this_actors0_connect0_node @@ -294,12 +307,12 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: { connect: [ - { where: { node: { name_EQ: "Daniel" } } } - { where: { node: { name_EQ: "Darrell" } } } + { where: { node: { name: { eq: "Daniel" } } } } + { where: { node: { name: { eq: "Darrell" } } } } ] } } @@ -314,7 +327,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -328,7 +342,7 @@ describe("Cypher Update", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect0_node - MERGE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) + CREATE (this)<-[this_actors0_connect0_relationship:ACTED_IN]-(this_actors0_connect0_node) } } WITH this, this_actors0_connect0_node @@ -346,7 +360,7 @@ describe("Cypher Update", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_actors0_connect1_node - MERGE (this)<-[this_actors0_connect1_relationship:ACTED_IN]-(this_actors0_connect1_node) + CREATE (this)<-[this_actors0_connect1_relationship:ACTED_IN]-(this_actors0_connect1_node) } } WITH this, this_actors0_connect1_node @@ -369,8 +383,8 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } - update: { actors: { disconnect: [{ where: { node: { name_EQ: "Daniel" } } }] } } + where: { id: { eq: "1" } } + update: { actors: { disconnect: [{ where: { node: { name: { eq: "Daniel" } } } }] } } ) { movies { id @@ -382,7 +396,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH this CALL { @@ -413,7 +428,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Daniel\\" + \\"name\\": { + \\"eq\\": \\"Daniel\\" + } } } } @@ -432,12 +449,12 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: { disconnect: [ - { where: { node: { name_EQ: "Daniel" } } } - { where: { node: { name_EQ: "Darrell" } } } + { where: { node: { name: { eq: "Daniel" } } } } + { where: { node: { name: { eq: "Darrell" } } } } ] } } @@ -452,7 +469,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH this CALL { @@ -497,14 +515,18 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Daniel\\" + \\"name\\": { + \\"eq\\": \\"Daniel\\" + } } } }, { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Darrell\\" + \\"name\\": { + \\"eq\\": \\"Darrell\\" + } } } } @@ -523,7 +545,7 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateActors( - where: { name_EQ: "Dan" } + where: { name: { eq: "Dan" } } update: { movies: { create: [{ node: { id: "dan_movie_id", title: "The Story of Beer" } }] } } ) { actors { @@ -540,7 +562,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH this CREATE (this_movies0_create0_node:Movie) @@ -551,6 +574,7 @@ describe("Cypher Update", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .id, .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -571,7 +595,7 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateActors( - where: { name_EQ: "Dan" } + where: { name: { eq: "Dan" } } update: { movies: { create: [{ node: { id: "dan_movie_id", title: "The Story of Beer" } }] } } ) { actors { @@ -588,7 +612,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH this CREATE (this_movies0_create0_node:Movie) @@ -599,6 +624,7 @@ describe("Cypher Update", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .id, .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -619,7 +645,7 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateActors( - where: { name_EQ: "Dan" } + where: { name: { eq: "Dan" } } update: { movies: { create: [ @@ -643,7 +669,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) + "CYPHER 5 + MATCH (this:Actor) WHERE this.name = $param0 WITH this CREATE (this_movies0_create0_node:Movie) @@ -658,6 +685,7 @@ describe("Cypher Update", () => { CALL { WITH this MATCH (this)-[update_this0:ACTED_IN]->(update_this1:Movie) + WITH DISTINCT update_this1 WITH update_this1 { .id, .title } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -680,10 +708,12 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: { - delete: { where: { node: { name_EQ: "Actor to delete" }, edge: { screenTime_EQ: 60 } } } + delete: { + where: { node: { name: { eq: "Actor to delete" } }, edge: { screenTime: { eq: 60 } } } + } } } ) { @@ -697,7 +727,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -731,12 +762,16 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" + \\"name\\": { + \\"eq\\": \\"Actor to delete\\" + } }, \\"edge\\": { - \\"screenTime_EQ\\": { - \\"low\\": 60, - \\"high\\": 0 + \\"screenTime\\": { + \\"eq\\": { + \\"low\\": 60, + \\"high\\": 0 + } } } } @@ -756,12 +791,12 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: { - where: { node: { name_EQ: "Actor to update" } } + where: { node: { name: { eq: "Actor to update" } } } update: { node: { name_SET: "Updated name" } } - delete: { where: { node: { name_EQ: "Actor to delete" } } } + delete: { where: { node: { name: { eq: "Actor to delete" } } } } } } ) { @@ -775,7 +810,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -813,7 +849,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor to update\\" + \\"name\\": { + \\"eq\\": \\"Actor to update\\" + } } }, \\"update\\": { @@ -825,7 +863,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" + \\"name\\": { + \\"eq\\": \\"Actor to delete\\" + } } } } @@ -844,8 +884,8 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } - update: { actors: { delete: { where: { node: { name_EQ: "Actor to delete" } } } } } + where: { id: { eq: "1" } } + update: { actors: { delete: { where: { node: { name: { eq: "Actor to delete" } } } } } } ) { movies { id @@ -857,7 +897,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -887,7 +928,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" + \\"name\\": { + \\"eq\\": \\"Actor to delete\\" + } } } } @@ -906,12 +949,12 @@ describe("Cypher Update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { actors: { delete: { - where: { node: { name_EQ: "Actor to delete" } } - delete: { movies: { where: { node: { id_EQ: "2" } } } } + where: { node: { name: { eq: "Actor to delete" } } } + delete: { movies: { where: { node: { id: { eq: "2" } } } } } } } } @@ -926,7 +969,8 @@ describe("Cypher Update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -969,7 +1013,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"Actor to delete\\" + \\"name\\": { + \\"eq\\": \\"Actor to delete\\" + } } }, \\"delete\\": { @@ -977,7 +1023,9 @@ describe("Cypher Update", () => { { \\"where\\": { \\"node\\": { - \\"id_EQ\\": \\"2\\" + \\"id\\": { + \\"eq\\": \\"2\\" + } } } } diff --git a/packages/graphql/tests/tck/pagination.test.ts b/packages/graphql/tests/tck/pagination.test.ts index 5c7a0ea7e7..dee2812f64 100644 --- a/packages/graphql/tests/tck/pagination.test.ts +++ b/packages/graphql/tests/tck/pagination.test.ts @@ -49,7 +49,8 @@ describe("Cypher pagination tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * SKIP $param0 RETURN this { .title } AS this" @@ -77,7 +78,8 @@ describe("Cypher pagination tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 RETURN this { .title } AS this" @@ -105,7 +107,8 @@ describe("Cypher pagination tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * SKIP $param0 LIMIT $param1 @@ -140,7 +143,8 @@ describe("Cypher pagination tests", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * SKIP $param0 LIMIT $param1 @@ -164,7 +168,7 @@ describe("Cypher pagination tests", () => { test("Skip + Limit with other variables", async () => { const query = /* GraphQL */ ` query ($offset: Int, $limit: Int, $title: String) { - movies(limit: $limit, offset: $offset, where: { title_EQ: $title }) { + movies(limit: $limit, offset: $offset, where: { title: { eq: $title } }) { title } } @@ -175,7 +179,8 @@ describe("Cypher pagination tests", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * SKIP $param1 diff --git a/packages/graphql/tests/tck/pagination/cypher-connection-pagination.test.ts b/packages/graphql/tests/tck/pagination/cypher-connection-pagination.test.ts index c6c9b3699f..3afd6e44e9 100644 --- a/packages/graphql/tests/tck/pagination/cypher-connection-pagination.test.ts +++ b/packages/graphql/tests/tck/pagination/cypher-connection-pagination.test.ts @@ -86,11 +86,6 @@ describe("Cypher Connection pagination", () => { neoSchema = new Neo4jGraphQL({ typeDefs, - features: { - excludeDeprecatedFields: { - deprecatedOptionsArgument: true, - }, - }, }); }); @@ -110,7 +105,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -154,7 +150,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -199,7 +196,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -244,7 +242,7 @@ describe("Cypher Connection pagination", () => { sort: [{ numberOfActors: DESC }, { title: ASC }] first: 10 after: "some-cursor" - where: { title_EQ: "The Matrix" } + where: { title: { eq: "The Matrix" } } ) { edges { cursor @@ -260,7 +258,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WHERE this0.title = $param0 WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -324,7 +323,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -392,7 +392,8 @@ describe("Cypher Connection pagination", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { diff --git a/packages/graphql/tests/tck/pagination/cypher-pagination.test.ts b/packages/graphql/tests/tck/pagination/cypher-pagination.test.ts index 5d18b5b473..d41032002b 100644 --- a/packages/graphql/tests/tck/pagination/cypher-pagination.test.ts +++ b/packages/graphql/tests/tck/pagination/cypher-pagination.test.ts @@ -82,11 +82,6 @@ describe("Cypher Sort tests", () => { neoSchema = new Neo4jGraphQL({ typeDefs, - features: { - excludeDeprecatedFields: { - deprecatedOptionsArgument: true, - }, - }, }); }); @@ -102,7 +97,8 @@ describe("Cypher Sort tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -134,7 +130,8 @@ describe("Cypher Sort tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -167,7 +164,8 @@ describe("Cypher Sort tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -204,7 +202,7 @@ describe("Cypher Sort tests", () => { sort: [{ numberOfActors: DESC }, { title: ASC }] offset: 10 limit: 10 - where: { title_EQ: "The Matrix" } + where: { title: { eq: "The Matrix" } } ) { id title @@ -215,7 +213,8 @@ describe("Cypher Sort tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -263,10 +262,12 @@ describe("Cypher Sort tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 CALL { WITH this1 CALL { diff --git a/packages/graphql/tests/tck/pagination/sort.test.ts b/packages/graphql/tests/tck/pagination/sort.test.ts index b6494779ff..ce6d5ca1f1 100644 --- a/packages/graphql/tests/tck/pagination/sort.test.ts +++ b/packages/graphql/tests/tck/pagination/sort.test.ts @@ -54,11 +54,6 @@ describe("Sort", () => { neoSchema = new Neo4jGraphQL({ typeDefs, - features: { - excludeDeprecatedFields: { - deprecatedOptionsArgument: true, - }, - }, }); }); @@ -75,7 +70,8 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id ASC RETURN this { .id, .title } AS this" @@ -97,7 +93,8 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC RETURN this { .id, .title } AS this" @@ -119,7 +116,8 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC RETURN this { .title, .id, aliased: this.id } AS this" @@ -141,7 +139,8 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * ORDER BY this.id DESC, this.title ASC RETURN this { .id, .title } AS this" @@ -164,10 +163,12 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 WITH this1 { .name } AS this1 ORDER BY this1.name ASC RETURN collect(this1) AS var2 @@ -192,10 +193,12 @@ describe("Sort", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this MATCH (this)-[this0:HAS_GENRE]->(this1:Genre) + WITH DISTINCT this1 WITH this1 { .name } AS this1 ORDER BY this1.name DESC RETURN collect(this1) AS var2 diff --git a/packages/graphql/tests/tck/pringles.test.ts b/packages/graphql/tests/tck/pringles.test.ts index b5ee4d1d65..d12b0c2b14 100644 --- a/packages/graphql/tests/tck/pringles.test.ts +++ b/packages/graphql/tests/tck/pringles.test.ts @@ -49,7 +49,7 @@ describe("Cypher Create Pringles", () => { id: ID! description: String! url: String! - color: Color! @relationship(type: "OF_COLOR", direction: OUT) + color: [Color!]! @relationship(type: "OF_COLOR", direction: OUT) } `; @@ -80,7 +80,7 @@ describe("Cypher Create Pringles", () => { id: 106 description: "Green photo" url: "g.png" - color: { connect: { where: { node: { id_EQ: "102" } } } } + color: { connect: { where: { node: { id: { eq: "102" } } } } } } } { @@ -88,7 +88,7 @@ describe("Cypher Create Pringles", () => { id: 107 description: "Red photo" url: "r.png" - color: { connect: { where: { node: { id_EQ: "100" } } } } + color: { connect: { where: { node: { id: { eq: "100" } } } } } } } ] @@ -106,7 +106,8 @@ describe("Cypher Create Pringles", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Product) SET this0.id = $this0_id SET this0.name = $this0_name @@ -137,14 +138,6 @@ describe("Cypher Create Pringles", () => { SET this0_photos0_node.url = $this0_photos0_node_url MERGE (this0)-[:HAS_PHOTO]->(this0_photos0_node) WITH * - CALL { - WITH this0_photos0_node - MATCH (this0_photos0_node)-[this0_photos0_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_photos0_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_photos0_node_color_Color_unique_ignored - } - WITH * CREATE (this0_photos1_node:Photo) SET this0_photos1_node.id = $this0_photos1_node_id SET this0_photos1_node.description = $this0_photos1_node_description @@ -161,7 +154,7 @@ describe("Cypher Create Pringles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_photos1_node UNWIND connectedNodes as this0_photos1_node_color_connect0_node - MERGE (this0_photos1_node)-[:OF_COLOR]->(this0_photos1_node_color_connect0_node) + CREATE (this0_photos1_node)-[:OF_COLOR]->(this0_photos1_node_color_connect0_node) } } WITH this0, this0_photos1_node, this0_photos1_node_color_connect0_node @@ -169,14 +162,6 @@ describe("Cypher Create Pringles", () => { } MERGE (this0)-[:HAS_PHOTO]->(this0_photos1_node) WITH * - CALL { - WITH this0_photos1_node - MATCH (this0_photos1_node)-[this0_photos1_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_photos1_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_photos1_node_color_Color_unique_ignored - } - WITH * CREATE (this0_photos2_node:Photo) SET this0_photos2_node.id = $this0_photos2_node_id SET this0_photos2_node.description = $this0_photos2_node_description @@ -193,21 +178,13 @@ describe("Cypher Create Pringles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0_photos2_node UNWIND connectedNodes as this0_photos2_node_color_connect0_node - MERGE (this0_photos2_node)-[:OF_COLOR]->(this0_photos2_node_color_connect0_node) + CREATE (this0_photos2_node)-[:OF_COLOR]->(this0_photos2_node_color_connect0_node) } } WITH this0, this0_photos2_node, this0_photos2_node_color_connect0_node RETURN count(*) AS connect_this0_photos2_node_color_connect_Color0 } MERGE (this0)-[:HAS_PHOTO]->(this0_photos2_node) - WITH * - CALL { - WITH this0_photos2_node - MATCH (this0_photos2_node)-[this0_photos2_node_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this0_photos2_node_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this0_photos2_node_color_Color_unique_ignored - } RETURN this0 } CALL { @@ -249,17 +226,17 @@ describe("Cypher Create Pringles", () => { const query = /* GraphQL */ ` mutation { updateProducts( - where: { name_EQ: "Pringles" } + where: { name: { eq: "Pringles" } } update: { photos: [ { - where: { node: { description_EQ: "Green Photo" } } + where: { node: { description: { eq: "Green Photo" } } } update: { node: { description_SET: "Light Green Photo" color: { - connect: { where: { node: { name_EQ: "Light Green" } } } - disconnect: { where: { node: { name_EQ: "Green" } } } + connect: { where: { node: { name: { eq: "Light Green" } } } } + disconnect: { where: { node: { name: { eq: "Green" } } } } } } } @@ -277,7 +254,8 @@ describe("Cypher Create Pringles", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Product) + "CYPHER 5 + MATCH (this:Product) WHERE this.name = $param0 WITH this CALL { @@ -289,7 +267,7 @@ describe("Cypher Create Pringles", () => { CALL { WITH this, this_photos0 OPTIONAL MATCH (this_photos0)-[this_photos0_color0_disconnect0_rel:OF_COLOR]->(this_photos0_color0_disconnect0:Color) - WHERE this_photos0_color0_disconnect0.name = $updateProducts_args_update_photos0_update_node_color_disconnect_where_Color_this_photos0_color0_disconnect0param0 + WHERE this_photos0_color0_disconnect0.name = $updateProducts_args_update_photos0_update_node_color0_disconnect0_where_Color_this_photos0_color0_disconnect0param0 CALL { WITH this_photos0_color0_disconnect0, this_photos0_color0_disconnect0_rel, this_photos0 WITH collect(this_photos0_color0_disconnect0) as this_photos0_color0_disconnect0, this_photos0_color0_disconnect0_rel, this_photos0 @@ -310,20 +288,12 @@ describe("Cypher Create Pringles", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this_photos0 UNWIND connectedNodes as this_photos0_color0_connect0_node - MERGE (this_photos0)-[:OF_COLOR]->(this_photos0_color0_connect0_node) + CREATE (this_photos0)-[:OF_COLOR]->(this_photos0_color0_connect0_node) } } WITH this, this_photos0, this_photos0_color0_connect0_node RETURN count(*) AS connect_this_photos0_color0_connect_Color0 } - WITH this, this_photos0 - CALL { - WITH this_photos0 - MATCH (this_photos0)-[this_photos0_color_Color_unique:OF_COLOR]->(:Color) - WITH count(this_photos0_color_Color_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDPhoto.color required exactly once', [0]) - RETURN c AS this_photos0_color_Color_unique_ignored - } RETURN count(*) AS update_this_photos0 } RETURN collect(DISTINCT this { .id }) AS data" @@ -334,7 +304,7 @@ describe("Cypher Create Pringles", () => { \\"param0\\": \\"Pringles\\", \\"updateProducts_args_update_photos0_where_this_photos0param0\\": \\"Green Photo\\", \\"this_update_photos0_description_SET\\": \\"Light Green Photo\\", - \\"updateProducts_args_update_photos0_update_node_color_disconnect_where_Color_this_photos0_color0_disconnect0param0\\": \\"Green\\", + \\"updateProducts_args_update_photos0_update_node_color0_disconnect0_where_Color_this_photos0_color0_disconnect0param0\\": \\"Green\\", \\"this_photos0_color0_connect0_node_param0\\": \\"Light Green\\", \\"updateProducts\\": { \\"args\\": { @@ -343,29 +313,40 @@ describe("Cypher Create Pringles", () => { { \\"where\\": { \\"node\\": { - \\"description_EQ\\": \\"Green Photo\\" + \\"description\\": { + \\"eq\\": \\"Green Photo\\" + } } }, \\"update\\": { \\"node\\": { \\"description_SET\\": \\"Light Green Photo\\", - \\"color\\": { - \\"connect\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Light Green\\" + \\"color\\": [ + { + \\"connect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Light Green\\" + } + } + } } - }, - \\"overwrite\\": true - }, - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"name_EQ\\": \\"Green\\" + ], + \\"disconnect\\": [ + { + \\"where\\": { + \\"node\\": { + \\"name\\": { + \\"eq\\": \\"Green\\" + } + } + } } - } + ] } - } + ] } } } diff --git a/packages/graphql/tests/tck/projection.test.ts b/packages/graphql/tests/tck/projection.test.ts index 3a1d801ba9..162801eea0 100644 --- a/packages/graphql/tests/tck/projection.test.ts +++ b/packages/graphql/tests/tck/projection.test.ts @@ -49,7 +49,7 @@ describe("Cypher Projection", () => { id: ID! description: String! url: String! - color: Color! @relationship(type: "OF_COLOR", direction: OUT) + color: [Color!]! @relationship(type: "OF_COLOR", direction: OUT) location: Point } `; @@ -65,7 +65,7 @@ describe("Cypher Projection", () => { createProducts(input: [{ id: "1" }, { id: "2" }]) { products { id - photos(where: { url_EQ: "url.com" }) { + photos(where: { url: { eq: "url.com" } }) { url location { latitude @@ -73,10 +73,10 @@ describe("Cypher Projection", () => { height } } - colors(where: { id_EQ: 123 }) { + colors(where: { id: { eq: 123 } }) { id } - sizes(where: { name_EQ: "small" }) { + sizes(where: { name: { eq: "small" } }) { name } } @@ -87,7 +87,8 @@ describe("Cypher Projection", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Product) @@ -99,6 +100,7 @@ describe("Cypher Projection", () => { WITH create_this1 MATCH (create_this1)-[create_this2:HAS_PHOTO]->(create_this3:Photo) WHERE create_this3.url = $create_param1 + WITH DISTINCT create_this3 WITH create_this3 { .url, .location } AS create_this3 RETURN collect(create_this3) AS create_var4 } @@ -106,6 +108,7 @@ describe("Cypher Projection", () => { WITH create_this1 MATCH (create_this1)-[create_this5:HAS_COLOR]->(create_this6:Color) WHERE create_this6.id = $create_param2 + WITH DISTINCT create_this6 WITH create_this6 { .id } AS create_this6 RETURN collect(create_this6) AS create_var7 } @@ -113,6 +116,7 @@ describe("Cypher Projection", () => { WITH create_this1 MATCH (create_this1)-[create_this8:HAS_SIZE]->(create_this9:Size) WHERE create_this9.name = $create_param3 + WITH DISTINCT create_this9 WITH create_this9 { .name } AS create_this9 RETURN collect(create_this9) AS create_var10 } diff --git a/packages/graphql/tests/tck/rfcs/query-limits.test.ts b/packages/graphql/tests/tck/rfcs/query-limits.test.ts index 46d0fb5015..2b53bb3801 100644 --- a/packages/graphql/tests/tck/rfcs/query-limits.test.ts +++ b/packages/graphql/tests/tck/rfcs/query-limits.test.ts @@ -63,7 +63,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 RETURN this { .id } AS this" @@ -91,7 +92,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Show) + "CYPHER 5 + MATCH (this:Show) WITH * LIMIT $param0 RETURN this { .id } AS this" @@ -119,7 +121,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Show) + "CYPHER 5 + MATCH (this:Show) WITH * LIMIT $param0 RETURN this { .id } AS this" @@ -152,12 +155,14 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) + WITH DISTINCT this1 WITH this1 { .id } AS this1 LIMIT $param1 RETURN collect(this1) AS var2 @@ -198,7 +203,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 CALL { @@ -252,7 +258,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 CALL { @@ -306,7 +313,8 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Festival) + "CYPHER 5 + MATCH (this:Festival) CALL { WITH this MATCH (this)<-[this0:PART_OF]-(this1:Show) @@ -350,12 +358,14 @@ describe("tck/rfcs/query-limits", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH * LIMIT $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) + WITH DISTINCT this1 WITH this1 { .id } AS this1 LIMIT $param1 RETURN collect(this1) AS var2 diff --git a/packages/graphql/tests/tck/rfcs/rfc-003.test.ts b/packages/graphql/tests/tck/rfcs/rfc-003.test.ts deleted file mode 100644 index b273b39603..0000000000 --- a/packages/graphql/tests/tck/rfcs/rfc-003.test.ts +++ /dev/null @@ -1,1529 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("tck/rfs/003", () => { - describe("one-to-one", () => { - describe("create", () => { - test("should add validation when creating node with a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-1"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}" }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 - CALL { - WITH create_var0 - CREATE (create_this1:Movie) - SET - create_this1.id = create_var0.id - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this2:DIRECTED]-(:Director) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c = 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once\\", [0]) - RETURN c AS create_var3 - } - RETURN create_this1 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"create_param0\\": [ - { - \\"id\\": \\"movieId-1\\" - } - ] - }" - `); - }); - - test("should add length validation when creating a node with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-1"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}" }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 - CALL { - WITH create_var0 - CREATE (create_this1:Movie) - SET - create_this1.id = create_var0.id - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this2:DIRECTED]-(:Director) - WITH count(create_this2) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one\\", [0]) - RETURN c AS create_var3 - } - RETURN create_this1 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"create_param0\\": [ - { - \\"id\\": \\"movieId-1\\" - } - ] - }" - `); - }); - - describe("nested mutations", () => { - test("should add validation when creating node with required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-2"; - const directorId = "directorId-2"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}", director: { create: { node: { id: "${directorId}" } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 - CALL { - WITH create_var0 - CREATE (create_this1:Movie) - SET - create_this1.id = create_var0.id - WITH create_this1, create_var0 - CALL { - WITH create_this1, create_var0 - UNWIND create_var0.director.create AS create_var2 - CREATE (create_this3:Director) - SET - create_this3.id = create_var2.node.id - MERGE (create_this1)<-[create_this4:DIRECTED]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:HAS_ADDRESS]->(:Address) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c = 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address required exactly once\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:DIRECTED]-(:Director) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c = 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once\\", [0]) - RETURN c AS create_var9 - } - RETURN create_this1 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"create_param0\\": [ - { - \\"id\\": \\"movieId-2\\", - \\"director\\": { - \\"create\\": { - \\"node\\": { - \\"id\\": \\"directorId-2\\" - } - } - } - } - ] - }" - `); - }); - - test("should add length validation when creating a node with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-2"; - const directorId = "directorId-2"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}", director: { create: { node: { id: "${directorId}" } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 - CALL { - WITH create_var0 - CREATE (create_this1:Movie) - SET - create_this1.id = create_var0.id - WITH create_this1, create_var0 - CALL { - WITH create_this1, create_var0 - UNWIND create_var0.director.create AS create_var2 - CREATE (create_this3:Director) - SET - create_this3.id = create_var2.node.id - MERGE (create_this1)<-[create_this4:DIRECTED]-(create_this3) - WITH create_this3 - CALL { - WITH create_this3 - MATCH (create_this3)-[create_this5:HAS_ADDRESS]->(:Address) - WITH count(create_this5) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address must be less than or equal to one\\", [0]) - RETURN c AS create_var6 - } - RETURN collect(NULL) AS create_var7 - } - WITH create_this1 - CALL { - WITH create_this1 - MATCH (create_this1)<-[create_this8:DIRECTED]-(:Director) - WITH count(create_this8) AS c - WHERE apoc.util.validatePredicate(NOT (c <= 1), \\"@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one\\", [0]) - RETURN c AS create_var9 - } - RETURN create_this1 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"create_param0\\": [ - { - \\"id\\": \\"movieId-2\\", - \\"director\\": { - \\"create\\": { - \\"node\\": { - \\"id\\": \\"directorId-2\\" - } - } - } - } - ] - }" - `); - }); - }); - - describe("update", () => { - test("should add validation when updating a node with a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies(where: { id_EQ: "${movieId}" }, update: { id_SET: "${movieId}" }) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - SET this.id = $this_update_id_SET - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-3\\", - \\"this_update_id_SET\\": \\"movieId-3\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add length validation when updating a node with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies(where: { id_EQ: "${movieId}" }, update: { id_SET: "${movieId}" }) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - SET this.id = $this_update_id_SET - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-3\\", - \\"this_update_id_SET\\": \\"movieId-3\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - describe("nested mutations", () => { - test("should add validation when updating a nested node with a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" } - update: { director: { update: { node: { id_SET: "${directorId}" } } } } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - MATCH (this)<-[this_directed0_relationship:DIRECTED]-(this_director0:Director) - SET this_director0.id = $this_update_director0_id_SET - WITH this, this_director0 - CALL { - WITH this_director0 - MATCH (this_director0)-[this_director0_address_Address_unique:HAS_ADDRESS]->(:Address) - WITH count(this_director0_address_Address_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address required exactly once', [0]) - RETURN c AS this_director0_address_Address_unique_ignored - } - RETURN count(*) AS update_this_director0 - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-4\\", - \\"this_update_director0_id_SET\\": \\"directorId-3\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add length validation when updating a nested node with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" } - update: { director: { update: { node: { id_SET: "${directorId}" } } } } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - MATCH (this)<-[this_directed0_relationship:DIRECTED]-(this_director0:Director) - SET this_director0.id = $this_update_director0_id_SET - WITH this, this_director0 - CALL { - WITH this_director0 - MATCH (this_director0)-[this_director0_address_Address_unique:HAS_ADDRESS]->(:Address) - WITH count(this_director0_address_Address_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address must be less than or equal to one', [0]) - RETURN c AS this_director0_address_Address_unique_ignored - } - RETURN count(*) AS update_this_director0 - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-4\\", - \\"this_update_director0_id_SET\\": \\"directorId-3\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add validation when creating a node with a required relationship through a nested mutation", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" } - update: { director: { create: { node: { id: "${directorId}" } } } } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - WITH * - WHERE apoc.util.validatePredicate(EXISTS((this)<-[:DIRECTED]-(:Director)),'Relationship field \\"%s.%s\\" cannot have more than one node linked',[\\"Movie\\",\\"director\\"]) - CREATE (this_director0_create0_node:Director) - SET this_director0_create0_node.id = $this_director0_create0_node_id - MERGE (this)<-[:DIRECTED]-(this_director0_create0_node) - WITH this, this_director0_create0_node - CALL { - WITH this_director0_create0_node - MATCH (this_director0_create0_node)-[this_director0_create0_node_address_Address_unique:HAS_ADDRESS]->(:Address) - WITH count(this_director0_create0_node_address_Address_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address required exactly once', [0]) - RETURN c AS this_director0_create0_node_address_Address_unique_ignored - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-4\\", - \\"this_director0_create0_node_id\\": \\"directorId-3\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - }); - - describe("delete", () => { - describe("nested mutations", () => { - test("should add validation when deleting a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - id: ID! - } - - type Director @node { - id: ID! - address: Address! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type CoDirector @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - coDirector: CoDirector @relationship(type: "CO_DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" }, - update: { - director: { - delete: { - where: { node: { id_EQ: "${directorId}" } }, - delete: { address: { where: { node: { id_EQ: "some-address" } } } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_director0_delete0_relationship:DIRECTED]-(this_director0_delete0:Director) - WHERE this_director0_delete0.id = $updateMovies_args_update_director_delete_where_this_director0_delete0param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this_director0_delete0)-[this_director0_delete0_address0_relationship:HAS_ADDRESS]->(this_director0_delete0_address0:Address) - WHERE this_director0_delete0_address0.id = $updateMovies_args_update_director_delete_delete_address_where_this_director0_delete0_address0param0 - WITH this_director0_delete0_address0_relationship, collect(DISTINCT this_director0_delete0_address0) AS this_director0_delete0_address0_to_delete - CALL { - WITH this_director0_delete0_address0_to_delete - UNWIND this_director0_delete0_address0_to_delete AS x - DETACH DELETE x - } - } - WITH this_director0_delete0_relationship, collect(DISTINCT this_director0_delete0) AS this_director0_delete0_to_delete - CALL { - WITH this_director0_delete0_to_delete - UNWIND this_director0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - CALL { - WITH this - MATCH (this)<-[this_coDirector_CoDirector_unique:CO_DIRECTED]-(:CoDirector) - WITH count(this_coDirector_CoDirector_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.coDirector must be less than or equal to one', [0]) - RETURN c AS this_coDirector_CoDirector_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-4\\", - \\"updateMovies_args_update_director_delete_where_this_director0_delete0param0\\": \\"directorId-3\\", - \\"updateMovies_args_update_director_delete_delete_address_where_this_director0_delete0_address0param0\\": \\"some-address\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"director\\": { - \\"delete\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId-3\\" - } - }, - \\"delete\\": { - \\"address\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"some-address\\" - } - } - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add length validation when deleting a node with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - id: ID! - } - - type Director @node { - id: ID! - address: Address @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type CoDirector @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - coDirector: CoDirector @relationship(type: "CO_DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-3"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" }, - update: { - director: { - delete: { - where: { node: { id_EQ: "${directorId}" } }, - delete: { address: { where: { node: { id_EQ: "some-address" } } } } - } - } - } - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)<-[this_director0_delete0_relationship:DIRECTED]-(this_director0_delete0:Director) - WHERE this_director0_delete0.id = $updateMovies_args_update_director_delete_where_this_director0_delete0param0 - WITH * - CALL { - WITH * - OPTIONAL MATCH (this_director0_delete0)-[this_director0_delete0_address0_relationship:HAS_ADDRESS]->(this_director0_delete0_address0:Address) - WHERE this_director0_delete0_address0.id = $updateMovies_args_update_director_delete_delete_address_where_this_director0_delete0_address0param0 - WITH this_director0_delete0_address0_relationship, collect(DISTINCT this_director0_delete0_address0) AS this_director0_delete0_address0_to_delete - CALL { - WITH this_director0_delete0_address0_to_delete - UNWIND this_director0_delete0_address0_to_delete AS x - DETACH DELETE x - } - } - WITH this_director0_delete0_relationship, collect(DISTINCT this_director0_delete0) AS this_director0_delete0_to_delete - CALL { - WITH this_director0_delete0_to_delete - UNWIND this_director0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one', [0]) - RETURN c AS this_director_Director_unique_ignored - } - CALL { - WITH this - MATCH (this)<-[this_coDirector_CoDirector_unique:CO_DIRECTED]-(:CoDirector) - WITH count(this_coDirector_CoDirector_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.coDirector must be less than or equal to one', [0]) - RETURN c AS this_coDirector_CoDirector_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-4\\", - \\"updateMovies_args_update_director_delete_where_this_director0_delete0param0\\": \\"directorId-3\\", - \\"updateMovies_args_update_director_delete_delete_address_where_this_director0_delete0_address0param0\\": \\"some-address\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"director\\": { - \\"delete\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId-3\\" - } - }, - \\"delete\\": { - \\"address\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"some-address\\" - } - } - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - }); - - describe("connect", () => { - test("should add validation when connecting to a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-4"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}", director: { connect: { where: { node: { id_EQ: "${directorId}" } } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_director_connect0_node:Director) - WHERE this0_director_connect0_node.id = $this0_director_connect0_node_param0 - CALL { - WITH * - WITH collect(this0_director_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_director_connect0_node - MERGE (this0)<-[:DIRECTED]-(this0_director_connect0_node) - } - } - WITH this0, this0_director_connect0_node - RETURN count(*) AS connect_this0_director_connect_Director0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_director_Director_unique:DIRECTED]-(:Director) - WITH count(this0_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this0_director_Director_unique_ignored - } - RETURN this0 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"movieId-4\\", - \\"this0_director_connect0_node_param0\\": \\"directorId-4\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add length validation when connecting to a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-4"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies(input: [{ id: "${movieId}", director: { connect: { where: { node: { id_EQ: "${directorId}" } } } } }]) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_director_connect0_node:Director) - WHERE this0_director_connect0_node.id = $this0_director_connect0_node_param0 - CALL { - WITH * - WITH collect(this0_director_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_director_connect0_node - MERGE (this0)<-[:DIRECTED]-(this0_director_connect0_node) - } - } - WITH this0, this0_director_connect0_node - RETURN count(*) AS connect_this0_director_connect_Director0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_director_Director_unique:DIRECTED]-(:Director) - WITH count(this0_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one', [0]) - RETURN c AS this0_director_Director_unique_ignored - } - RETURN this0 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"movieId-4\\", - \\"this0_director_connect0_node_param0\\": \\"directorId-4\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - - describe("nested mutations", () => { - test("should add validation when connecting to a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Address @node { - street: String! - } - - type Director @node { - id: ID! - address: Address! @relationship(type: "HAS_ADDRESS", direction: OUT) - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-4"; - const directorId = "directorId-4"; - - const mutation = /* GraphQL */ ` - mutation { - createMovies( - input: [ - { - id: "${movieId}" - director: { - connect: { - where: { node: { id_EQ: "${directorId}" } } - connect: { address: { where: { node: { street_EQ: "some-street" } } } } - } - } - } - ] - ) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { - CREATE (this0:Movie) - SET this0.id = $this0_id - WITH * - CALL { - WITH this0 - OPTIONAL MATCH (this0_director_connect0_node:Director) - WHERE this0_director_connect0_node.id = $this0_director_connect0_node_param0 - CALL { - WITH * - WITH collect(this0_director_connect0_node) as connectedNodes, collect(this0) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0 - UNWIND connectedNodes as this0_director_connect0_node - MERGE (this0)<-[:DIRECTED]-(this0_director_connect0_node) - } - } - WITH this0, this0_director_connect0_node - CALL { - WITH this0, this0_director_connect0_node - OPTIONAL MATCH (this0_director_connect0_node_address0_node:Address) - WHERE this0_director_connect0_node_address0_node.street = $this0_director_connect0_node_address0_node_param0 - CALL { - WITH * - WITH this0, collect(this0_director_connect0_node_address0_node) as connectedNodes, collect(this0_director_connect0_node) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this0_director_connect0_node - UNWIND connectedNodes as this0_director_connect0_node_address0_node - MERGE (this0_director_connect0_node)-[:HAS_ADDRESS]->(this0_director_connect0_node_address0_node) - } - } - WITH this0, this0_director_connect0_node, this0_director_connect0_node_address0_node - CALL { - WITH this0_director_connect0_node - MATCH (this0_director_connect0_node)-[this0_director_connect0_node_address_Address_unique:HAS_ADDRESS]->(:Address) - WITH count(this0_director_connect0_node_address_Address_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDDirector.address required exactly once', [0]) - RETURN c AS this0_director_connect0_node_address_Address_unique_ignored - } - WITH this0, this0_director_connect0_node, this0_director_connect0_node_address0_node - RETURN count(*) AS connect_this0_director_connect0_node_address_Address0 - } - RETURN count(*) AS connect_this0_director_connect_Director0 - } - WITH * - CALL { - WITH this0 - MATCH (this0)<-[this0_director_Director_unique:DIRECTED]-(:Director) - WITH count(this0_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this0_director_Director_unique_ignored - } - RETURN this0 - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"this0_id\\": \\"movieId-4\\", - \\"this0_director_connect0_node_param0\\": \\"directorId-4\\", - \\"this0_director_connect0_node_address0_node_param0\\": \\"some-street\\", - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - }); - - describe("disconnect", () => { - test("should add validation when disconnecting from a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-5"; - const directorId = "directorId-5"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies(where: { id_EQ: "${movieId}" }, update: { director: { disconnect: { where: { node: { id_EQ: "${directorId}" } } } } }) { - info { - nodesCreated - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_director0_disconnect0_rel:DIRECTED]-(this_director0_disconnect0:Director) - WHERE this_director0_disconnect0.id = $updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0 - CALL { - WITH this_director0_disconnect0, this_director0_disconnect0_rel, this - WITH collect(this_director0_disconnect0) as this_director0_disconnect0, this_director0_disconnect0_rel, this - UNWIND this_director0_disconnect0 as x - DELETE this_director0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_director0_disconnect_Director - } - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - RETURN \\"Query cannot conclude with CALL\\"" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-5\\", - \\"updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0\\": \\"directorId-5\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"director\\": { - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId-5\\" - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - - describe("reconnect", () => { - test("should add validation after disconnecting and connecting with a required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director! @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-6"; - const directorId1 = "directorId-6"; - const directorId2 = "directorId2-6"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" }, - update: { - director: { - disconnect: { - where: { node: { id_EQ: "${directorId1}" } } - } - connect: { - where: { node: { id_EQ: "${directorId2}" } } - } - } - } - ) { - movies { - id - director { - id - } - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_director0_disconnect0_rel:DIRECTED]-(this_director0_disconnect0:Director) - WHERE this_director0_disconnect0.id = $updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0 - CALL { - WITH this_director0_disconnect0, this_director0_disconnect0_rel, this - WITH collect(this_director0_disconnect0) as this_director0_disconnect0, this_director0_disconnect0_rel, this - UNWIND this_director0_disconnect0 as x - DELETE this_director0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_director0_disconnect_Director - } - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_director0_connect0_node:Director) - WHERE this_director0_connect0_node.id = $this_director0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_director0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_director0_connect0_node - MERGE (this)<-[:DIRECTED]-(this_director0_connect0_node) - } - } - WITH this, this_director0_connect0_node - RETURN count(*) AS connect_this_director0_connect_Director0 - } - WITH * - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c = 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director required exactly once', [0]) - RETURN c AS this_director_Director_unique_ignored - } - CALL { - WITH this - MATCH (this)<-[update_this0:DIRECTED]-(update_this1:Director) - WITH update_this1 { .id } AS update_this1 - RETURN head(collect(update_this1)) AS update_var2 - } - RETURN collect(DISTINCT this { .id, director: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-6\\", - \\"updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0\\": \\"directorId-6\\", - \\"this_director0_connect0_node_param0\\": \\"directorId2-6\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"director\\": { - \\"connect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId2-6\\" - } - }, - \\"overwrite\\": true - }, - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId-6\\" - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("should add validation after disconnecting and connecting with a non required relationship", async () => { - const typeDefs = /* GraphQL */ ` - type Director @node { - id: ID! - } - - type Movie @node { - id: ID! - director: Director @relationship(type: "DIRECTED", direction: IN) - } - `; - - const neoSchema = new Neo4jGraphQL({ typeDefs }); - - const movieId = "movieId-6"; - const directorId1 = "directorId-6"; - const directorId2 = "directorId2-6"; - - const mutation = /* GraphQL */ ` - mutation { - updateMovies( - where: { id_EQ: "${movieId}" }, - update: { - director: { - disconnect: { - where: { node: { id_EQ: "${directorId1}" } } - } - connect: { - where: { node: { id_EQ: "${directorId2}" } } - } - } - } - ) { - movies { - id - director { - id - } - } - } - } - `; - - const result = await translateQuery(neoSchema, mutation); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.id = $param0 - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)<-[this_director0_disconnect0_rel:DIRECTED]-(this_director0_disconnect0:Director) - WHERE this_director0_disconnect0.id = $updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0 - CALL { - WITH this_director0_disconnect0, this_director0_disconnect0_rel, this - WITH collect(this_director0_disconnect0) as this_director0_disconnect0, this_director0_disconnect0_rel, this - UNWIND this_director0_disconnect0 as x - DELETE this_director0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_director0_disconnect_Director - } - WITH * - CALL { - WITH this - OPTIONAL MATCH (this_director0_connect0_node:Director) - WHERE this_director0_connect0_node.id = $this_director0_connect0_node_param0 - CALL { - WITH * - WITH collect(this_director0_connect0_node) as connectedNodes, collect(this) as parentNodes - CALL { - WITH connectedNodes, parentNodes - UNWIND parentNodes as this - UNWIND connectedNodes as this_director0_connect0_node - MERGE (this)<-[:DIRECTED]-(this_director0_connect0_node) - } - } - WITH this, this_director0_connect0_node - RETURN count(*) AS connect_this_director0_connect_Director0 - } - WITH * - WITH * - CALL { - WITH this - MATCH (this)<-[this_director_Director_unique:DIRECTED]-(:Director) - WITH count(this_director_Director_unique) as c - WHERE apoc.util.validatePredicate(NOT (c <= 1), '@neo4j/graphql/RELATIONSHIP-REQUIREDMovie.director must be less than or equal to one', [0]) - RETURN c AS this_director_Director_unique_ignored - } - CALL { - WITH this - MATCH (this)<-[update_this0:DIRECTED]-(update_this1:Director) - WITH update_this1 { .id } AS update_this1 - RETURN head(collect(update_this1)) AS update_var2 - } - RETURN collect(DISTINCT this { .id, director: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"movieId-6\\", - \\"updateMovies_args_update_director_disconnect_where_Director_this_director0_disconnect0param0\\": \\"directorId-6\\", - \\"this_director0_connect0_node_param0\\": \\"directorId2-6\\", - \\"updateMovies\\": { - \\"args\\": { - \\"update\\": { - \\"director\\": { - \\"connect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId2-6\\" - } - }, - \\"overwrite\\": true - }, - \\"disconnect\\": { - \\"where\\": { - \\"node\\": { - \\"id_EQ\\": \\"directorId-6\\" - } - } - } - } - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - }); - }); - }); -}); diff --git a/packages/graphql/tests/tck/rfcs/rfc-022.test.ts b/packages/graphql/tests/tck/rfcs/rfc-022.test.ts index b2b2a8ae3c..3db9bd6c8e 100644 --- a/packages/graphql/tests/tck/rfcs/rfc-022.test.ts +++ b/packages/graphql/tests/tck/rfcs/rfc-022.test.ts @@ -54,9 +54,9 @@ describe("tck/rfs/022 subquery projection", () => { test("Nested query", async () => { const query = /* GraphQL */ ` query Query { - movies(where: { released_EQ: 1999 }) { + movies(where: { released: { eq: 1999 } }) { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -66,12 +66,14 @@ describe("tck/rfs/022 subquery projection", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.released = $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) WHERE this1.name = $param1 + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -92,9 +94,9 @@ describe("tck/rfs/022 subquery projection", () => { test("Double nested query", async () => { const query = /* GraphQL */ ` query Query { - movies(where: { released_EQ: 1999 }) { + movies(where: { released: { eq: 1999 } }) { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name directed { title @@ -108,15 +110,18 @@ describe("tck/rfs/022 subquery projection", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.released = $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) WHERE this1.name = $param1 + WITH DISTINCT this1 CALL { WITH this1 MATCH (this1)-[this2:DIRECTED]->(this3:Movie) + WITH DISTINCT this3 WITH this3 { .title, .released } AS this3 RETURN collect(this3) AS var4 } @@ -154,9 +159,12 @@ describe("tck/rfs/022 subquery projection", () => { type Person @node @authorization( - filter: [{ where: { node: { name_EQ: "The Matrix" } } }] + filter: [{ where: { node: { name: { eq: "The Matrix" } } } }] validate: [ - { when: [BEFORE], where: { node: { name_EQ: "$jwt.test" }, jwt: { roles_INCLUDES: "admin" } } } + { + when: [BEFORE] + where: { node: { name: { eq: "$jwt.test" } }, jwt: { roles: { includes: "admin" } } } + } ] ) { name: String! @@ -177,9 +185,9 @@ describe("tck/rfs/022 subquery projection", () => { test("Nested query", async () => { const query = /* GraphQL */ ` query Query { - movies(where: { released_EQ: 1999 }) { + movies(where: { released: { eq: 1999 } }) { title - actors(where: { name_EQ: "Keanu Reeves" }) { + actors(where: { name: { eq: "Keanu Reeves" } }) { name } } @@ -194,11 +202,13 @@ describe("tck/rfs/022 subquery projection", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.released = $param0 CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Person) + WITH DISTINCT this1 WITH * WHERE (this1.name = $param1 AND ($isAuthenticated = true AND ($param3 IS NOT NULL AND this1.name = $param3)) AND apoc.util.validatePredicate(NOT ($isAuthenticated = true AND ($jwt.test IS NOT NULL AND this1.name = $jwt.test) AND ($jwt.roles IS NOT NULL AND $param5 IN $jwt.roles)), \\"@neo4j/graphql/FORBIDDEN\\", [0])) WITH this1 { .name } AS this1 diff --git a/packages/graphql/tests/tck/root-connection.test.ts b/packages/graphql/tests/tck/root-connection.test.ts index b3fac7a5dc..1cb287f4ca 100644 --- a/packages/graphql/tests/tck/root-connection.test.ts +++ b/packages/graphql/tests/tck/root-connection.test.ts @@ -46,7 +46,7 @@ describe("Root Connection Query tests", () => { test("Simple selection, Movie by title", async () => { const query = /* GraphQL */ ` { - moviesConnection(where: { title_EQ: "River Runs Through It, A" }) { + moviesConnection(where: { title: { eq: "River Runs Through It, A" } }) { totalCount edges { node { @@ -60,7 +60,8 @@ describe("Root Connection Query tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WHERE this0.title = $param0 WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -96,7 +97,8 @@ describe("Root Connection Query tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -134,7 +136,8 @@ describe("Root Connection Query tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WHERE this0.title CONTAINS $param0 WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount @@ -181,7 +184,8 @@ describe("Root Connection Query tests", () => { `; const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this0:Movie) + "CYPHER 5 + MATCH (this0:Movie) WITH collect({ node: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { diff --git a/packages/graphql/tests/tck/simple.test.ts b/packages/graphql/tests/tck/simple.test.ts index 40c0066096..74a45b609b 100644 --- a/packages/graphql/tests/tck/simple.test.ts +++ b/packages/graphql/tests/tck/simple.test.ts @@ -40,7 +40,7 @@ describe("Simple Cypher tests", () => { test("Single selection, Movie by title", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "River Runs Through It, A" }) { + movies(where: { title: { eq: "River Runs Through It, A" } }) { title } } @@ -49,7 +49,8 @@ describe("Simple Cypher tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -64,7 +65,7 @@ describe("Simple Cypher tests", () => { test("Multi selection, Movie by title", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "River Runs Through It, A" }) { + movies(where: { title: { eq: "River Runs Through It, A" } }) { id title } @@ -74,7 +75,8 @@ describe("Simple Cypher tests", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .id, .title } AS this" `); @@ -89,7 +91,7 @@ describe("Simple Cypher tests", () => { test("Multi selection, Movie by title via variable", async () => { const query = /* GraphQL */ ` query ($title: String) { - movies(where: { title_EQ: $title }) { + movies(where: { title: { eq: $title } }) { id title } @@ -101,7 +103,8 @@ describe("Simple Cypher tests", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .id, .title } AS this" `); diff --git a/packages/graphql/tests/tck/subscriptions/create-auth.test.ts b/packages/graphql/tests/tck/subscriptions/create-auth.test.ts index 60be4d6642..4dd5944521 100644 --- a/packages/graphql/tests/tck/subscriptions/create-auth.test.ts +++ b/packages/graphql/tests/tck/subscriptions/create-auth.test.ts @@ -38,7 +38,7 @@ describe("Subscriptions metadata on create", () => { actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN) } - extend type Actor @authorization(validate: [{ when: [AFTER], where: { node: { id_EQ: "$jwt.sub" } } }]) + extend type Actor @authorization(validate: [{ when: [AFTER], where: { node: { id: { eq: "$jwt.sub" } } } }]) `; neoSchema = new Neo4jGraphQL({ @@ -68,7 +68,8 @@ describe("Subscriptions metadata on create", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Actor) diff --git a/packages/graphql/tests/tck/subscriptions/create.test.ts b/packages/graphql/tests/tck/subscriptions/create.test.ts index 5d5c3cc8aa..b535c8abf9 100644 --- a/packages/graphql/tests/tck/subscriptions/create.test.ts +++ b/packages/graphql/tests/tck/subscriptions/create.test.ts @@ -101,7 +101,8 @@ describe("Subscriptions metadata on create", () => { ); // TODO: make a test with rel type as union/ interface expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -205,7 +206,8 @@ describe("Subscriptions metadata on create", () => { ); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -325,7 +327,8 @@ describe("Subscriptions metadata on create", () => { ); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -480,7 +483,8 @@ describe("Subscriptions metadata on create", () => { ); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -619,7 +623,8 @@ describe("Subscriptions metadata on create", () => { ); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -654,6 +659,7 @@ describe("Subscriptions metadata on create", () => { CALL { WITH create_this4 MATCH (create_this4)-[create_this5:ACTED_IN]->(create_this6:Movie) + WITH DISTINCT create_this6 WITH create_this6 { .title } AS create_this6 RETURN collect(create_this6) AS create_var7 } @@ -704,7 +710,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -740,7 +747,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -782,7 +790,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -803,6 +812,7 @@ describe("Subscriptions metadata on create", () => { CALL { WITH create_this1 MATCH (create_this1)<-[create_this6:ACTED_IN]-(create_this7:Actor) + WITH DISTINCT create_this7 WITH create_this7 { .name } AS create_this7 RETURN collect(create_this7) AS create_var8 } @@ -853,7 +863,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -884,6 +895,7 @@ describe("Subscriptions metadata on create", () => { CALL { WITH create_this1 MATCH (create_this1)<-[create_this10:ACTED_IN]-(create_this11:Actor) + WITH DISTINCT create_this11 WITH create_this11 { .name } AS create_this11 RETURN collect(create_this11) AS create_var12 } @@ -960,7 +972,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -1001,12 +1014,15 @@ describe("Subscriptions metadata on create", () => { CALL { WITH create_this1 MATCH (create_this1)<-[create_this14:ACTED_IN]-(create_this15:Actor) + WITH DISTINCT create_this15 CALL { WITH create_this15 MATCH (create_this15)-[create_this16:ACTED_IN]->(create_this17:Movie) + WITH DISTINCT create_this17 CALL { WITH create_this17 MATCH (create_this17)<-[create_this18:ACTED_IN]-(create_this19:Actor) + WITH DISTINCT create_this19 WITH create_this19 { .name } AS create_this19 RETURN collect(create_this19) AS create_var20 } @@ -1082,7 +1098,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -1177,7 +1194,8 @@ describe("Subscriptions metadata on create", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) diff --git a/packages/graphql/tests/tck/subscriptions/delete.test.ts b/packages/graphql/tests/tck/subscriptions/delete.test.ts index d4db531e51..a1942d5787 100644 --- a/packages/graphql/tests/tck/subscriptions/delete.test.ts +++ b/packages/graphql/tests/tck/subscriptions/delete.test.ts @@ -49,7 +49,7 @@ describe("Subscriptions metadata on delete", () => { test("Simple delete", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: "1" }) { + deleteMovies(where: { id: { eq: "1" } }) { nodesDeleted } } @@ -58,7 +58,8 @@ describe("Subscriptions metadata on delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 DETACH DELETE this" `); @@ -73,7 +74,10 @@ describe("Subscriptions metadata on delete", () => { test("Nested delete", async () => { const query = /* GraphQL */ ` mutation { - deleteMovies(where: { id_EQ: "1" }, delete: { actors: { where: { node: { name_EQ: "1" } } } }) { + deleteMovies( + where: { id: { eq: "1" } } + delete: { actors: { where: { node: { name: { eq: "1" } } } } } + ) { nodesDeleted } } @@ -82,7 +86,8 @@ describe("Subscriptions metadata on delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { @@ -111,14 +116,14 @@ describe("Subscriptions metadata on delete", () => { const query = /* GraphQL */ ` mutation { deleteMovies( - where: { id_EQ: 123 } + where: { id: { eq: 123 } } delete: { actors: { - where: { node: { name_EQ: "Actor to delete" } } + where: { node: { name: { eq: "Actor to delete" } } } delete: { movies: { - where: { node: { id_EQ: 321 } } - delete: { actors: { where: { node: { name_EQ: "Another actor to delete" } } } } + where: { node: { id: { eq: 321 } } } + delete: { actors: { where: { node: { name: { eq: "Another actor to delete" } } } } } } } } @@ -132,7 +137,8 @@ describe("Subscriptions metadata on delete", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 WITH * CALL { diff --git a/packages/graphql/tests/tck/subscriptions/update.test.ts b/packages/graphql/tests/tck/subscriptions/update.test.ts index 16e377daa9..1c08fd0fb0 100644 --- a/packages/graphql/tests/tck/subscriptions/update.test.ts +++ b/packages/graphql/tests/tck/subscriptions/update.test.ts @@ -49,7 +49,7 @@ describe("Subscriptions metadata on update", () => { test("Simple update with subscriptions", async () => { const query = /* GraphQL */ ` mutation { - updateMovies(where: { id_EQ: "1" }, update: { id_SET: "2" }) { + updateMovies(where: { id: { eq: "1" } }, update: { id_SET: "2" }) { movies { id } @@ -60,7 +60,8 @@ describe("Subscriptions metadata on update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 SET this.id = $this_update_id_SET RETURN collect(DISTINCT this { .id }) AS data" @@ -79,10 +80,12 @@ describe("Subscriptions metadata on update", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { id_EQ: "1" } + where: { id: { eq: "1" } } update: { id_SET: "2" - actors: [{ where: { node: { name_EQ: "arthur" } }, update: { node: { name_SET: "ford" } } }] + actors: [ + { where: { node: { name: { eq: "arthur" } } }, update: { node: { name_SET: "ford" } } } + ] } ) { movies { @@ -95,7 +98,8 @@ describe("Subscriptions metadata on update", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.id = $param0 SET this.id = $this_update_id_SET WITH this @@ -123,7 +127,9 @@ describe("Subscriptions metadata on update", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"arthur\\" + \\"name\\": { + \\"eq\\": \\"arthur\\" + } } }, \\"update\\": { diff --git a/packages/graphql/tests/tck/types/bigint.test.ts b/packages/graphql/tests/tck/types/bigint.test.ts index 3e07ead095..49509154a7 100644 --- a/packages/graphql/tests/tck/types/bigint.test.ts +++ b/packages/graphql/tests/tck/types/bigint.test.ts @@ -40,7 +40,7 @@ describe("Cypher BigInt", () => { test("Querying with native BigInt in AST", async () => { const query = /* GraphQL */ ` query { - files(where: { size_EQ: 9223372036854775807 }) { + files(where: { size: { eq: 9223372036854775807 } }) { name } } @@ -49,7 +49,8 @@ describe("Cypher BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:File) + "CYPHER 5 + MATCH (this:File) WHERE this.size = $param0 RETURN this { .name } AS this" `); @@ -67,7 +68,7 @@ describe("Cypher BigInt", () => { test("Querying with BigInt as string in AST", async () => { const query = /* GraphQL */ ` query { - files(where: { size_EQ: "9223372036854775807" }) { + files(where: { size: { eq: "9223372036854775807" } }) { name } } @@ -76,7 +77,8 @@ describe("Cypher BigInt", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:File) + "CYPHER 5 + MATCH (this:File) WHERE this.size = $param0 RETURN this { .name } AS this" `); @@ -94,7 +96,7 @@ describe("Cypher BigInt", () => { test("Querying with BigInt as string in variables", async () => { const query = /* GraphQL */ ` query Files($size: BigInt) { - files(where: { size_EQ: $size }) { + files(where: { size: { eq: $size } }) { name } } @@ -105,7 +107,8 @@ describe("Cypher BigInt", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:File) + "CYPHER 5 + MATCH (this:File) WHERE this.size = $param0 RETURN this { .name } AS this" `); diff --git a/packages/graphql/tests/tck/types/date.test.ts b/packages/graphql/tests/tck/types/date.test.ts index ceba74c061..0bf19fb98d 100644 --- a/packages/graphql/tests/tck/types/date.test.ts +++ b/packages/graphql/tests/tck/types/date.test.ts @@ -40,7 +40,7 @@ describe("Cypher Date", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { date_EQ: "1970-01-01" }) { + movies(where: { date: { eq: "1970-01-01" } }) { date } } @@ -49,7 +49,8 @@ describe("Cypher Date", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.date = $param0 RETURN this { .date } AS this" `); @@ -68,7 +69,7 @@ describe("Cypher Date", () => { test("GTE Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { date_GTE: "1980-04-08" }) { + movies(where: { date: { gte: "1980-04-08" } }) { date } } @@ -77,7 +78,8 @@ describe("Cypher Date", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.date >= $param0 RETURN this { .date } AS this" `); @@ -107,7 +109,8 @@ describe("Cypher Date", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -148,7 +151,8 @@ describe("Cypher Date", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) SET this.date = $this_update_date_SET RETURN collect(DISTINCT this { .id, .date }) AS data" `); diff --git a/packages/graphql/tests/tck/types/datetime.test.ts b/packages/graphql/tests/tck/types/datetime.test.ts index 8a31284608..9681a10ce5 100644 --- a/packages/graphql/tests/tck/types/datetime.test.ts +++ b/packages/graphql/tests/tck/types/datetime.test.ts @@ -40,7 +40,7 @@ describe("Cypher DateTime", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { datetime_EQ: "1970-01-01T00:00:00.000Z" }) { + movies(where: { datetime: { eq: "1970-01-01T00:00:00.000Z" } }) { datetime } } @@ -49,23 +49,15 @@ describe("Cypher DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.datetime = $param0 + "CYPHER 5 + MATCH (this:Movie) + WHERE this.datetime = datetime($param0) RETURN this { datetime: apoc.date.convertFormat(toString(this.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"1970-01-01T00:00:00.000Z\\" }" `); }); @@ -84,12 +76,13 @@ describe("Cypher DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.datetime = create_var0.datetime + create_this1.datetime = datetime(create_var0.datetime) RETURN create_this1 } RETURN collect(create_this1 { datetime: apoc.date.convertFormat(toString(create_this1.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") }) AS data" @@ -99,16 +92,7 @@ describe("Cypher DateTime", () => { "{ \\"create_param0\\": [ { - \\"datetime\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"datetime\\": \\"1970-01-01T00:00:00.000Z\\" } ] }" @@ -130,23 +114,15 @@ describe("Cypher DateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.datetime = $this_update_datetime_SET + "CYPHER 5 + MATCH (this:Movie) + SET this.datetime = datetime($this_update_datetime_SET) RETURN collect(DISTINCT this { .id, datetime: apoc.date.convertFormat(toString(this.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"this_update_datetime_SET\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, + \\"this_update_datetime_SET\\": \\"1970-01-01T00:00:00.000Z\\", \\"resolvedCallbacks\\": {} }" `); diff --git a/packages/graphql/tests/tck/types/duration.test.ts b/packages/graphql/tests/tck/types/duration.test.ts index f45c4baa35..40437ab06b 100644 --- a/packages/graphql/tests/tck/types/duration.test.ts +++ b/packages/graphql/tests/tck/types/duration.test.ts @@ -40,7 +40,7 @@ describe("Cypher Duration", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { duration_EQ: "P1Y" }) { + movies(where: { duration: { eq: "P1Y" } }) { duration } } @@ -49,7 +49,8 @@ describe("Cypher Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.duration = $param0 RETURN this { .duration } AS this" `); @@ -75,7 +76,7 @@ describe("Cypher Duration", () => { test("GTE Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { duration_GTE: "P3Y4M" }) { + movies(where: { duration: { gte: "P3Y4M" } }) { duration } } @@ -84,7 +85,8 @@ describe("Cypher Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (datetime() + this.duration) >= (datetime() + $param0) RETURN this { .duration } AS this" `); @@ -121,7 +123,8 @@ describe("Cypher Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -169,7 +172,8 @@ describe("Cypher Duration", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) SET this.duration = $this_update_duration_SET RETURN collect(DISTINCT this { .id, .duration }) AS data" `); diff --git a/packages/graphql/tests/tck/types/localdatetime.test.ts b/packages/graphql/tests/tck/types/localdatetime.test.ts index 298e9beb90..d3c43f1a06 100644 --- a/packages/graphql/tests/tck/types/localdatetime.test.ts +++ b/packages/graphql/tests/tck/types/localdatetime.test.ts @@ -40,7 +40,7 @@ describe("Cypher LocalDateTime", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { localDT_EQ: "2003-09-14T12:00:00" }) { + movies(where: { localDT: { eq: "2003-09-14T12:00:00" } }) { localDT } } @@ -49,7 +49,8 @@ describe("Cypher LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.localDT = $param0 RETURN this { .localDT } AS this" `); @@ -72,7 +73,7 @@ describe("Cypher LocalDateTime", () => { test("GTE Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { localDT_GTE: "2010-08-23T13:45:33.250" }) { + movies(where: { localDT: { gte: "2010-08-23T13:45:33.250" } }) { localDT } } @@ -81,7 +82,8 @@ describe("Cypher LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.localDT >= $param0 RETURN this { .localDT } AS this" `); @@ -115,7 +117,8 @@ describe("Cypher LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -160,7 +163,8 @@ describe("Cypher LocalDateTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) SET this.localDT = $this_update_localDT_SET RETURN collect(DISTINCT this { .id, .localDT }) AS data" `); diff --git a/packages/graphql/tests/tck/types/localtime.test.ts b/packages/graphql/tests/tck/types/localtime.test.ts index 8290fe047b..1e58e52aa5 100644 --- a/packages/graphql/tests/tck/types/localtime.test.ts +++ b/packages/graphql/tests/tck/types/localtime.test.ts @@ -40,7 +40,7 @@ describe("Cypher LocalTime", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { time_EQ: "12:00:00" }) { + movies(where: { time: { eq: "12:00:00" } }) { time } } @@ -49,7 +49,8 @@ describe("Cypher LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.time = $param0 RETURN this { .time } AS this" `); @@ -69,7 +70,7 @@ describe("Cypher LocalTime", () => { test("GTE Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { time_GTE: "13:45:33.250" }) { + movies(where: { time: { gte: "13:45:33.250" } }) { time } } @@ -78,7 +79,8 @@ describe("Cypher LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.time >= $param0 RETURN this { .time } AS this" `); @@ -109,7 +111,8 @@ describe("Cypher LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) @@ -151,7 +154,8 @@ describe("Cypher LocalTime", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) SET this.time = $this_update_time_SET RETURN collect(DISTINCT this { .id, .time }) AS data" `); diff --git a/packages/graphql/tests/tck/types/point.test.ts b/packages/graphql/tests/tck/types/point.test.ts index 3aec6e70d1..1e6393a39e 100644 --- a/packages/graphql/tests/tck/types/point.test.ts +++ b/packages/graphql/tests/tck/types/point.test.ts @@ -40,7 +40,7 @@ describe("Cypher Points", () => { test("Simple Point query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_EQ: { longitude: 1.0, latitude: 2.0 } }) { + pointContainers(where: { point: { eq: { longitude: 1.0, latitude: 2.0 } } }) { point { longitude latitude @@ -53,7 +53,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE this.point = point($param0) RETURN this { .point } AS this" `); @@ -71,7 +72,7 @@ describe("Cypher Points", () => { test("Simple Point NOT query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { NOT: { point_EQ: { longitude: 1.0, latitude: 2.0 } } }) { + pointContainers(where: { NOT: { point: { eq: { longitude: 1.0, latitude: 2.0 } } } }) { point { longitude latitude @@ -83,7 +84,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE NOT (this.point = point($param0)) RETURN this { .point } AS this" `); @@ -101,7 +103,7 @@ describe("Cypher Points", () => { test("Simple Point IN query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_IN: [{ longitude: 1.0, latitude: 2.0 }] }) { + pointContainers(where: { point: { in: [{ longitude: 1.0, latitude: 2.0 }] } }) { point { longitude latitude @@ -114,7 +116,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE this.point IN [var0 IN $param0 | point(var0)] RETURN this { .point } AS this" `); @@ -135,7 +138,9 @@ describe("Cypher Points", () => { test("Simple Point LT query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_LT: { point: { longitude: 1.1, latitude: 2.2 }, distance: 3.3 } }) { + pointContainers( + where: { point: { distance: { from: { longitude: 1.1, latitude: 2.2 }, lt: 3.3 } } } + ) { point { longitude latitude @@ -147,7 +152,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point.distance(this.point, point($param0.point)) < $param0.distance RETURN this { .point } AS this" `); @@ -155,11 +161,11 @@ describe("Cypher Points", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { + \\"distance\\": 3.3, \\"point\\": { \\"longitude\\": 1.1, \\"latitude\\": 2.2 - }, - \\"distance\\": 3.3 + } } }" `); @@ -168,7 +174,9 @@ describe("Cypher Points", () => { test("Simple Point LTE query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_LTE: { point: { longitude: 1.1, latitude: 2.2 }, distance: 3.3 } }) { + pointContainers( + where: { point: { distance: { from: { longitude: 1.1, latitude: 2.2 }, lte: 3.3 } } } + ) { point { longitude latitude @@ -180,7 +188,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point.distance(this.point, point($param0.point)) <= $param0.distance RETURN this { .point } AS this" `); @@ -188,11 +197,11 @@ describe("Cypher Points", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { + \\"distance\\": 3.3, \\"point\\": { \\"longitude\\": 1.1, \\"latitude\\": 2.2 - }, - \\"distance\\": 3.3 + } } }" `); @@ -201,7 +210,9 @@ describe("Cypher Points", () => { test("Simple Point GT query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_GT: { point: { longitude: 1.1, latitude: 2.2 }, distance: 3.3 } }) { + pointContainers( + where: { point: { distance: { from: { longitude: 1.1, latitude: 2.2 }, gt: 3.3 } } } + ) { point { longitude latitude @@ -213,7 +224,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point.distance(this.point, point($param0.point)) > $param0.distance RETURN this { .point } AS this" `); @@ -221,11 +233,11 @@ describe("Cypher Points", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { + \\"distance\\": 3.3, \\"point\\": { \\"longitude\\": 1.1, \\"latitude\\": 2.2 - }, - \\"distance\\": 3.3 + } } }" `); @@ -234,7 +246,9 @@ describe("Cypher Points", () => { test("Simple Point GTE query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { point_GTE: { point: { longitude: 1.1, latitude: 2.2 }, distance: 3.3 } }) { + pointContainers( + where: { point: { distance: { from: { longitude: 1.1, latitude: 2.2 }, gte: 3.3 } } } + ) { point { longitude latitude @@ -246,7 +260,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point.distance(this.point, point($param0.point)) >= $param0.distance RETURN this { .point } AS this" `); @@ -254,11 +269,11 @@ describe("Cypher Points", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { + \\"distance\\": 3.3, \\"point\\": { \\"longitude\\": 1.1, \\"latitude\\": 2.2 - }, - \\"distance\\": 3.3 + } } }" `); @@ -268,7 +283,7 @@ describe("Cypher Points", () => { const query = /* GraphQL */ ` { pointContainers( - where: { point_DISTANCE: { point: { longitude: 1.1, latitude: 2.2 }, distance: 3.3 } } + where: { point: { distance: { from: { longitude: 1.1, latitude: 2.2 }, eq: 3.3 } } } ) { point { longitude @@ -281,7 +296,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point.distance(this.point, point($param0.point)) = $param0.distance RETURN this { .point } AS this" `); @@ -289,11 +305,11 @@ describe("Cypher Points", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ \\"param0\\": { + \\"distance\\": 3.3, \\"point\\": { \\"longitude\\": 1.1, \\"latitude\\": 2.2 - }, - \\"distance\\": 3.3 + } } }" `); @@ -318,7 +334,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:PointContainer) @@ -347,7 +364,7 @@ describe("Cypher Points", () => { const query = /* GraphQL */ ` mutation { updatePointContainers( - where: { id_EQ: "id" } + where: { id: { eq: "id" } } update: { point_SET: { longitude: 1.0, latitude: 2.0 } } ) { pointContainers { @@ -364,7 +381,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE this.id = $param0 SET this.point = point($this_update_point_SET) RETURN collect(DISTINCT this { .point }) AS data" diff --git a/packages/graphql/tests/tck/types/points.test.ts b/packages/graphql/tests/tck/types/points.test.ts index 26ed953e60..68c56e628c 100644 --- a/packages/graphql/tests/tck/types/points.test.ts +++ b/packages/graphql/tests/tck/types/points.test.ts @@ -28,7 +28,7 @@ describe("Cypher Points", () => { typeDefs = /* GraphQL */ ` type PointContainer @node { id: String - points: [Point] + points: [Point!] } `; @@ -40,7 +40,7 @@ describe("Cypher Points", () => { test("Simple Points query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { points_EQ: [{ longitude: 1.0, latitude: 2.0 }] }) { + pointContainers(where: { points: { eq: [{ longitude: 1.0, latitude: 2.0 }] } }) { points { longitude latitude @@ -53,7 +53,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE this.points = [var0 IN $param0 | point(var0)] RETURN this { .points } AS this" `); @@ -73,7 +74,7 @@ describe("Cypher Points", () => { test("Simple Points NOT query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { NOT: { points_EQ: [{ longitude: 1.0, latitude: 2.0 }] } }) { + pointContainers(where: { NOT: { points: { eq: [{ longitude: 1.0, latitude: 2.0 }] } } }) { points { longitude latitude @@ -85,7 +86,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE NOT (this.points = [var0 IN $param0 | point(var0)]) RETURN this { .points } AS this" `); @@ -105,7 +107,7 @@ describe("Cypher Points", () => { test("Simple Points INCLUDES query", async () => { const query = /* GraphQL */ ` { - pointContainers(where: { points_INCLUDES: { longitude: 1.0, latitude: 2.0 } }) { + pointContainers(where: { points: { includes: { longitude: 1.0, latitude: 2.0 } } }) { points { longitude latitude @@ -118,7 +120,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE point($param0) IN this.points RETURN this { .points } AS this" `); @@ -151,7 +154,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:PointContainer) @@ -182,7 +186,7 @@ describe("Cypher Points", () => { const query = /* GraphQL */ ` mutation { updatePointContainers( - where: { id_EQ: "id" } + where: { id: { eq: "id" } } update: { points_SET: [{ longitude: 1.0, latitude: 2.0 }] } ) { pointContainers { @@ -199,7 +203,8 @@ describe("Cypher Points", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:PointContainer) + "CYPHER 5 + MATCH (this:PointContainer) WHERE this.id = $param0 SET this.points = [p in $this_update_points_SET | point(p)] RETURN collect(DISTINCT this { .points }) AS data" diff --git a/packages/graphql/tests/tck/types/time.test.ts b/packages/graphql/tests/tck/types/time.test.ts index 1a9608a017..eb6d9868e3 100644 --- a/packages/graphql/tests/tck/types/time.test.ts +++ b/packages/graphql/tests/tck/types/time.test.ts @@ -40,7 +40,7 @@ describe("Cypher Time", () => { test("Simple Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { time_EQ: "12:00:00" }) { + movies(where: { time: { eq: "12:00:00" } }) { time } } @@ -49,20 +49,15 @@ describe("Cypher Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.time = $param0 + "CYPHER 5 + MATCH (this:Movie) + WHERE this.time = time($param0) RETURN this { .time } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -70,7 +65,7 @@ describe("Cypher Time", () => { test("GTE Read", async () => { const query = /* GraphQL */ ` query { - movies(where: { time_GTE: "13:45:33.250" }) { + movies(where: { time: { gte: "13:45:33.250" } }) { time } } @@ -79,20 +74,15 @@ describe("Cypher Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - WHERE this.time >= $param0 + "CYPHER 5 + MATCH (this:Movie) + WHERE this.time >= time($param0) RETURN this { .time } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 13, - \\"minute\\": 45, - \\"second\\": 33, - \\"nanosecond\\": 250000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"13:45:33.250\\" }" `); }); @@ -111,12 +101,13 @@ describe("Cypher Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.time = create_var0.time + create_this1.time = time(create_var0.time) RETURN create_this1 } RETURN collect(create_this1 { .time }) AS data" @@ -126,13 +117,7 @@ describe("Cypher Time", () => { "{ \\"create_param0\\": [ { - \\"time\\": { - \\"hour\\": 22, - \\"minute\\": 0, - \\"second\\": 15, - \\"nanosecond\\": 555000000, - \\"timeZoneOffsetSeconds\\": -3600 - } + \\"time\\": \\"22:00:15.555-01:00\\" } ] }" @@ -154,20 +139,15 @@ describe("Cypher Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) - SET this.time = $this_update_time_SET + "CYPHER 5 + MATCH (this:Movie) + SET this.time = time($this_update_time_SET) RETURN collect(DISTINCT this { .id, .time }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"this_update_time_SET\\": { - \\"hour\\": 9, - \\"minute\\": 24, - \\"second\\": 40, - \\"nanosecond\\": 845512000, - \\"timeZoneOffsetSeconds\\": 23400 - }, + \\"this_update_time_SET\\": \\"09:24:40.845512+06:30\\", \\"resolvedCallbacks\\": {} }" `); @@ -187,12 +167,13 @@ describe("Cypher Time", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "UNWIND $create_param0 AS create_var0 + "CYPHER 5 + UNWIND $create_param0 AS create_var0 CALL { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.time = create_var0.time + create_this1.time = time(create_var0.time) RETURN create_this1 } RETURN collect(create_this1 { .time }) AS data" @@ -202,13 +183,7 @@ describe("Cypher Time", () => { "{ \\"create_param0\\": [ { - \\"time\\": { - \\"hour\\": 22, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"time\\": \\"22:00\\" } ] }" diff --git a/packages/graphql/tests/tck/undirected-relationships/query-direction-aggregations.test.ts b/packages/graphql/tests/tck/undirected-relationships/query-direction-aggregations.test.ts index 687cd87298..f045afcc9f 100644 --- a/packages/graphql/tests/tck/undirected-relationships/query-direction-aggregations.test.ts +++ b/packages/graphql/tests/tck/undirected-relationships/query-direction-aggregations.test.ts @@ -48,7 +48,8 @@ describe("QueryDirection in relationships aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) @@ -83,7 +84,8 @@ describe("QueryDirection in relationships aggregations", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) diff --git a/packages/graphql/tests/tck/undirected-relationships/query-direction-connection.test.ts b/packages/graphql/tests/tck/undirected-relationships/query-direction-connection.test.ts index e150da7195..38dd3daa10 100644 --- a/packages/graphql/tests/tck/undirected-relationships/query-direction-connection.test.ts +++ b/packages/graphql/tests/tck/undirected-relationships/query-direction-connection.test.ts @@ -48,7 +48,8 @@ describe("QueryDirection in relationships connection", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) @@ -92,7 +93,8 @@ describe("QueryDirection in relationships connection", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) diff --git a/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts b/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts index 7ae1c1d04c..ec124c9a0b 100644 --- a/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts +++ b/packages/graphql/tests/tck/undirected-relationships/query-direction.test.ts @@ -21,14 +21,14 @@ import { Neo4jGraphQL } from "../../../src"; import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; describe("queryDirection in relationships", () => { - describe("DIRECTED_ONLY", () => { + describe("DIRECTED", () => { let neoSchema: Neo4jGraphQL; beforeAll(() => { const typeDefs = /* GraphQL */ ` type User @node { name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED_ONLY) + friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DIRECTED) } `; @@ -52,10 +52,12 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -68,7 +70,7 @@ describe("queryDirection in relationships", () => { test("query with filter", async () => { const query = /* GraphQL */ ` query { - users(where: { friends_SOME: { name: "John Smith" } }) { + users(where: { friends: { some: { name: { eq: "John Smith" } } } }) { name friends { name @@ -80,7 +82,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]->(this0:User) WHERE this0.name = $param0 @@ -88,6 +91,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -105,8 +109,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + update: { friends: { disconnect: { where: { node: { name: { eq: "Jane Smith" } } } } } } ) { users { name @@ -121,7 +125,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]->(this0:User) WHERE this0.name = $param0 @@ -143,6 +148,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -162,7 +168,9 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" + } } } } @@ -181,8 +189,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + update: { friends: { delete: { where: { node: { name: { eq: "Jane Smith" } } } } } } ) { users { name @@ -197,7 +205,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]->(this0:User) WHERE this0.name = $param0 @@ -218,6 +227,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -237,7 +247,9 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" + } } } } @@ -256,11 +268,11 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } + where: { friends: { some: { name: { eq: "John Smith" } } } } update: { friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } + where: { node: { name: { eq: "Jane Smith" } } } + update: { node: { name_SET: "Janet Smith" } } } } ) { @@ -277,7 +289,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]->(this0:User) WHERE this0.name = $param0 @@ -287,13 +300,14 @@ describe("queryDirection in relationships", () => { WITH this MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name + SET this_friends0.name = $this_update_friends0_name_SET RETURN count(*) AS update_this_friends0 } WITH * CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -304,7 +318,7 @@ describe("queryDirection in relationships", () => { "{ \\"param0\\": \\"John Smith\\", \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", + \\"this_update_friends0_name_SET\\": \\"Janet Smith\\", \\"updateUsers\\": { \\"args\\": { \\"update\\": { @@ -312,721 +326,14 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"Janet Smith\\" - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("delete with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } - ) { - nodesDeleted - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WHERE this2.name = $param1 - WITH this1, collect(DISTINCT this2) AS var3 - CALL { - WITH var3 - UNWIND var3 AS var4 - DETACH DELETE var4 - } - } - WITH * - DETACH DELETE this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"param1\\": \\"Jane Smith\\" - }" - `); - }); - }); - - describe("DEFAULT_DIRECTED", () => { - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - const typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_DIRECTED) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("query", async () => { - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]->(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with filter", async () => { - const query = /* GraphQL */ ` - query { - users(where: { friends_SOME: { name: "John Smith" } }) { - name - friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - CALL { - WITH this - MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - RETURN this { .name, friends: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\" - }" - `); - }); - - test("disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]->(this_friends0_disconnect0:User) - WHERE this_friends0_disconnect0.name = $updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0 - CALL { - WITH this_friends0_disconnect0, this_friends0_disconnect0_rel, this - WITH collect(this_friends0_disconnect0) as this_friends0_disconnect0, this_friends0_disconnect0_rel, this - UNWIND this_friends0_disconnect0 as x - DELETE this_friends0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_friends0_disconnect_User - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]->(this_friends0_delete0:User) - WHERE this_friends0_delete0.name = $updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0 - WITH this_friends0_delete0_relationship, collect(DISTINCT this_friends0_delete0) AS this_friends0_delete0_to_delete - CALL { - WITH this_friends0_delete0_to_delete - UNWIND this_friends0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { - friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } - } - } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) - WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name - RETURN count(*) AS update_this_friends0 - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]->(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - }, - \\"update\\": { - \\"node\\": { - \\"name\\": \\"Janet Smith\\" - } - } - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("delete with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } - ) { - nodesDeleted - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]->(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) - WHERE this2.name = $param1 - WITH this1, collect(DISTINCT this2) AS var3 - CALL { - WITH var3 - UNWIND var3 AS var4 - DETACH DELETE var4 - } - } - WITH * - DETACH DELETE this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"param1\\": \\"Jane Smith\\" - }" - `); - }); - }); - describe("UNDIRECTED_ONLY", () => { - let neoSchema: Neo4jGraphQL; - - beforeAll(() => { - const typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED_ONLY) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - }); - - test("query", async () => { - const query = /* GraphQL */ ` - query { - users { - name - friends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - RETURN this { .name, friends: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("query with filter", async () => { - const query = /* GraphQL */ ` - query { - users(where: { friends_SOME: { name: "John Smith" } }) { - name - friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - CALL { - WITH this - MATCH (this)-[this1:FRIENDS_WITH]-(this2:User) - WITH this2 { .name } AS this2 - RETURN collect(this2) AS var3 - } - RETURN this { .name, friends: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\" - }" - `); - }); - - test("disconnect", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]->(this_friends0_disconnect0:User) - WHERE this_friends0_disconnect0.name = $updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0 - CALL { - WITH this_friends0_disconnect0, this_friends0_disconnect0_rel, this - WITH collect(this_friends0_disconnect0) as this_friends0_disconnect0, this_friends0_disconnect0_rel, this - UNWIND this_friends0_disconnect0 as x - DELETE this_friends0_disconnect0_rel - } - RETURN count(*) AS disconnect_this_friends0_disconnect_User - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"disconnect\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } - } - } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update with nested delete", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH * - CALL { - WITH * - OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]->(this_friends0_delete0:User) - WHERE this_friends0_delete0.name = $updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0 - WITH this_friends0_delete0_relationship, collect(DISTINCT this_friends0_delete0) AS this_friends0_delete0_to_delete - CALL { - WITH this_friends0_delete0_to_delete - UNWIND this_friends0_delete0_to_delete AS x - DETACH DELETE x - } - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0\\": \\"Jane Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"delete\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" } } - ] - } - ] - } - } - }, - \\"resolvedCallbacks\\": {} - }" - `); - }); - - test("update", async () => { - const query = /* GraphQL */ ` - mutation { - updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { - friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } - } - } - ) { - users { - name - friends { - name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) - WHERE this0.name = $param0 - } - WITH this - CALL { - WITH this - MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) - WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name - RETURN count(*) AS update_this_friends0 - } - WITH * - CALL { - WITH this - MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) - WITH update_this1 { .name } AS update_this1 - RETURN collect(update_this1) AS update_var2 - } - RETURN collect(DISTINCT this { .name, friends: update_var2 }) AS data" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(` - "{ - \\"param0\\": \\"John Smith\\", - \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", - \\"updateUsers\\": { - \\"args\\": { - \\"update\\": { - \\"friends\\": [ - { - \\"where\\": { - \\"node\\": { - \\"name\\": \\"Jane Smith\\" - } }, \\"update\\": { \\"node\\": { - \\"name\\": \\"Janet Smith\\" + \\"name_SET\\": \\"Janet Smith\\" } } } @@ -1043,8 +350,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + delete: { friends: { where: { node: { name: { eq: "Jane Smith" } } } } } ) { nodesDeleted } @@ -1054,9 +361,10 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { - MATCH (this)-[:FRIENDS_WITH]-(this0:User) + MATCH (this)-[:FRIENDS_WITH]->(this0:User) WHERE this0.name = $param0 } WITH * @@ -1084,15 +392,14 @@ describe("queryDirection in relationships", () => { }); }); - describe("DEFAULT_UNDIRECTED", () => { + describe("UNDIRECTED", () => { let neoSchema: Neo4jGraphQL; beforeAll(() => { const typeDefs = /* GraphQL */ ` type User @node { name: String! - friends: [User!]! - @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: DEFAULT_UNDIRECTED) + friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT, queryDirection: UNDIRECTED) } `; @@ -1116,10 +423,12 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) CALL { WITH this MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) + WITH DISTINCT this1 WITH this1 { .name } AS this1 RETURN collect(this1) AS var2 } @@ -1132,7 +441,7 @@ describe("queryDirection in relationships", () => { test("query with filter", async () => { const query = /* GraphQL */ ` query { - users(where: { friends_SOME: { name: "John Smith" } }) { + users(where: { friends: { some: { name: { eq: "John Smith" } } } }) { name friends { name @@ -1144,7 +453,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]-(this0:User) WHERE this0.name = $param0 @@ -1152,6 +462,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[this1:FRIENDS_WITH]-(this2:User) + WITH DISTINCT this2 WITH this2 { .name } AS this2 RETURN collect(this2) AS var3 } @@ -1169,8 +480,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { disconnect: { where: { node: { name: "Jane Smith" } } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + update: { friends: { disconnect: { where: { node: { name: { eq: "Jane Smith" } } } } } } ) { users { name @@ -1185,7 +496,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]-(this0:User) WHERE this0.name = $param0 @@ -1193,7 +505,7 @@ describe("queryDirection in relationships", () => { WITH this CALL { WITH this - OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]->(this_friends0_disconnect0:User) + OPTIONAL MATCH (this)-[this_friends0_disconnect0_rel:FRIENDS_WITH]-(this_friends0_disconnect0:User) WHERE this_friends0_disconnect0.name = $updateUsers_args_update_friends0_disconnect0_where_User_this_friends0_disconnect0param0 CALL { WITH this_friends0_disconnect0, this_friends0_disconnect0_rel, this @@ -1207,6 +519,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -1226,7 +539,9 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" + } } } } @@ -1245,8 +560,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } - update: { friends: { delete: { where: { node: { name: "Jane Smith" } } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + update: { friends: { delete: { where: { node: { name: { eq: "Jane Smith" } } } } } } ) { users { name @@ -1261,7 +576,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]-(this0:User) WHERE this0.name = $param0 @@ -1269,7 +585,7 @@ describe("queryDirection in relationships", () => { WITH * CALL { WITH * - OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]->(this_friends0_delete0:User) + OPTIONAL MATCH (this)-[this_friends0_delete0_relationship:FRIENDS_WITH]-(this_friends0_delete0:User) WHERE this_friends0_delete0.name = $updateUsers_args_update_friends0_delete0_where_this_friends0_delete0param0 WITH this_friends0_delete0_relationship, collect(DISTINCT this_friends0_delete0) AS this_friends0_delete0_to_delete CALL { @@ -1282,6 +598,7 @@ describe("queryDirection in relationships", () => { CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -1301,7 +618,9 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" + } } } } @@ -1320,11 +639,11 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { updateUsers( - where: { friends_SOME: { name: "John Smith" } } + where: { friends: { some: { name: { eq: "John Smith" } } } } update: { friends: { - where: { node: { name: "Jane Smith" } } - update: { node: { name: "Janet Smith" } } + where: { node: { name: { eq: "Jane Smith" } } } + update: { node: { name_SET: "Janet Smith" } } } } ) { @@ -1341,7 +660,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]-(this0:User) WHERE this0.name = $param0 @@ -1349,15 +669,16 @@ describe("queryDirection in relationships", () => { WITH this CALL { WITH this - MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]->(this_friends0:User) + MATCH (this)-[this_friends_with0_relationship:FRIENDS_WITH]-(this_friends0:User) WHERE this_friends0.name = $updateUsers_args_update_friends0_where_this_friends0param0 - SET this_friends0.name = $this_update_friends0_name + SET this_friends0.name = $this_update_friends0_name_SET RETURN count(*) AS update_this_friends0 } WITH * CALL { WITH this MATCH (this)-[update_this0:FRIENDS_WITH]-(update_this1:User) + WITH DISTINCT update_this1 WITH update_this1 { .name } AS update_this1 RETURN collect(update_this1) AS update_var2 } @@ -1368,7 +689,7 @@ describe("queryDirection in relationships", () => { "{ \\"param0\\": \\"John Smith\\", \\"updateUsers_args_update_friends0_where_this_friends0param0\\": \\"Jane Smith\\", - \\"this_update_friends0_name\\": \\"Janet Smith\\", + \\"this_update_friends0_name_SET\\": \\"Janet Smith\\", \\"updateUsers\\": { \\"args\\": { \\"update\\": { @@ -1376,12 +697,14 @@ describe("queryDirection in relationships", () => { { \\"where\\": { \\"node\\": { - \\"name\\": \\"Jane Smith\\" + \\"name\\": { + \\"eq\\": \\"Jane Smith\\" + } } }, \\"update\\": { \\"node\\": { - \\"name\\": \\"Janet Smith\\" + \\"name_SET\\": \\"Janet Smith\\" } } } @@ -1398,8 +721,8 @@ describe("queryDirection in relationships", () => { const query = /* GraphQL */ ` mutation { deleteUsers( - where: { friends_SOME: { name: "John Smith" } } - delete: { friends: { where: { node: { name: "Jane Smith" } } } } + where: { friends: { some: { name: { eq: "John Smith" } } } } + delete: { friends: { where: { node: { name: { eq: "Jane Smith" } } } } } ) { nodesDeleted } @@ -1409,7 +732,8 @@ describe("queryDirection in relationships", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) + "CYPHER 5 + MATCH (this:User) WHERE EXISTS { MATCH (this)-[:FRIENDS_WITH]-(this0:User) WHERE this0.name = $param0 @@ -1417,7 +741,7 @@ describe("queryDirection in relationships", () => { WITH * CALL { WITH * - OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]->(this2:User) + OPTIONAL MATCH (this)-[this1:FRIENDS_WITH]-(this2:User) WHERE this2.name = $param1 WITH this1, collect(DISTINCT this2) AS var3 CALL { diff --git a/packages/graphql/tests/tck/undirected-relationships/undirected-aggregations.test.ts b/packages/graphql/tests/tck/undirected-relationships/undirected-aggregations.test.ts deleted file mode 100644 index 26d8e83a70..0000000000 --- a/packages/graphql/tests/tck/undirected-relationships/undirected-aggregations.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Undirected Aggregations", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with undirected aggregation", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Users { - users { - friendsAggregate(directed: false) { - count - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - RETURN count(this1) AS var2 - } - RETURN this { friendsAggregate: { count: var2 } } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/undirected-relationships/undirected-connection.test.ts b/packages/graphql/tests/tck/undirected-relationships/undirected-connection.test.ts deleted file mode 100644 index cc4f2a868d..0000000000 --- a/packages/graphql/tests/tck/undirected-relationships/undirected-connection.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Undirected connections", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with undirected aggregation", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query FriendsAggregate { - users { - friendsConnection(directed: false) { - totalCount - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH collect({ node: this1, relationship: this0 }) AS edges - WITH edges, size(edges) AS totalCount - CALL { - WITH edges - UNWIND edges AS edge - WITH edge.node AS this1, edge.relationship AS this0 - RETURN collect({ node: { __id: id(this1), __resolveType: \\"User\\" } }) AS var2 - } - RETURN { edges: var2, totalCount: totalCount } AS var3 - } - RETURN this { friendsConnection: var3 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts b/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts deleted file mode 100644 index 855493aa5d..0000000000 --- a/packages/graphql/tests/tck/undirected-relationships/undirected-relationships.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Neo4jGraphQL } from "../../../src"; -import { formatCypher, formatParams, translateQuery } from "../utils/tck-test-utils"; - -describe("Undirected relationships", () => { - let typeDefs: string; - let neoSchema: Neo4jGraphQL; - - test("query with directed and undirected relationships", async () => { - typeDefs = /* GraphQL */ ` - type User @node { - name: String! - friends: [User!]! @relationship(type: "FRIENDS_WITH", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query { - users { - name - friends: friends(directed: false) { - name - } - directedFriends: friends { - name - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - MATCH (this)-[this0:FRIENDS_WITH]-(this1:User) - WITH this1 { .name } AS this1 - RETURN collect(this1) AS var2 - } - CALL { - WITH this - MATCH (this)-[this3:FRIENDS_WITH]->(this4:User) - WITH this4 { .name } AS this4 - RETURN collect(this4) AS var5 - } - RETURN this { .name, friends: var2, directedFriends: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("undirected with unions", async () => { - typeDefs = /* GraphQL */ ` - union Content = Blog | Post - - type Blog @node { - title: String - posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT) - } - - type Post @node { - content: String - } - - type User @node { - name: String - content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Users { - users { - content(directed: false) { - ... on Blog { - title - } - ... on Post { - content - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:User) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this0:HAS_CONTENT]-(this1:Blog) - WITH this1 { .title, __resolveType: \\"Blog\\", __id: id(this1) } AS this1 - RETURN this1 AS var2 - UNION - WITH * - MATCH (this)-[this3:HAS_CONTENT]-(this4:Post) - WITH this4 { .content, __resolveType: \\"Post\\", __id: id(this4) } AS this4 - RETURN this4 AS var2 - } - WITH var2 - RETURN collect(var2) AS var2 - } - RETURN this { content: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("undirected with interfaces", async () => { - typeDefs = /* GraphQL */ ` - interface Production { - title: String! - actors: [Actor!]! - } - - type Movie implements Production @node { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - runtime: Int! - } - - type Series implements Production @node { - title: String! - actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") - episodes: Int! - } - - type ActedIn @relationshipProperties { - role: String! - } - - type Actor @node { - name: String! - actedIn: [Production!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn") - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Actors { - actors { - actedIn(directed: false) { - title - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Actor) - CALL { - WITH this - CALL { - WITH * - MATCH (this)-[this0:ACTED_IN]-(this1:Movie) - WITH this1 { .title, __resolveType: \\"Movie\\", __id: id(this1) } AS this1 - RETURN this1 AS var2 - UNION - WITH * - MATCH (this)-[this3:ACTED_IN]-(this4:Series) - WITH this4 { .title, __resolveType: \\"Series\\", __id: id(this4) } AS this4 - RETURN this4 AS var2 - } - WITH var2 - RETURN collect(var2) AS var2 - } - RETURN this { actedIn: var2 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); - - test("nested undirected relationship", async () => { - typeDefs = /* GraphQL */ ` - type Foo @node { - id: ID @unique - Name: String - Age: Int - DrinksAt: Bar @relationship(type: "DRINKS_AT", direction: OUT) - } - - type Bar @node { - id: ID @unique - Adress: String - Customers: [Foo!]! @relationship(type: "DRINKS_AT", direction: IN) - } - `; - - neoSchema = new Neo4jGraphQL({ - typeDefs, - }); - const query = /* GraphQL */ ` - query Query { - foos { - DrinksAt { - id - Customers(directed: false) { - Name - } - } - } - } - `; - - const result = await translateQuery(neoSchema, query); - - expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Foo) - CALL { - WITH this - MATCH (this)-[this0:DRINKS_AT]->(this1:Bar) - CALL { - WITH this1 - MATCH (this1)-[this2:DRINKS_AT]-(this3:Foo) - WITH this3 { .Name } AS this3 - RETURN collect(this3) AS var4 - } - WITH this1 { .id, Customers: var4 } AS this1 - RETURN head(collect(this1)) AS var5 - } - RETURN this { DrinksAt: var5 } AS this" - `); - - expect(formatParams(result.params)).toMatchInlineSnapshot(`"{}"`); - }); -}); diff --git a/packages/graphql/tests/tck/union.test.ts b/packages/graphql/tests/tck/union.test.ts index 26957f6318..2a651e8a2f 100644 --- a/packages/graphql/tests/tck/union.test.ts +++ b/packages/graphql/tests/tck/union.test.ts @@ -37,7 +37,7 @@ describe("Cypher Union", () => { { when: [BEFORE] operations: [READ] - where: { node: { name_EQ: "$jwt.jwtAllowedNamesExample" } } + where: { node: { name: { eq: "$jwt.jwtAllowedNamesExample" } } } } ] ) { @@ -76,7 +76,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -125,7 +126,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) CALL { WITH this CALL { @@ -160,9 +162,9 @@ describe("Cypher Union", () => { test("Read Unions with filter and limit", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: "some title" }) { + movies(where: { title: { eq: "some title" } }) { search( - where: { Movie: { title_EQ: "The Matrix" }, Genre: { name_EQ: "Horror" } } + where: { Movie: { title: { eq: "The Matrix" } }, Genre: { name: { eq: "Horror" } } } offset: 1 limit: 10 ) { @@ -181,7 +183,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 CALL { WITH this @@ -245,7 +248,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -285,7 +289,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WITH this CREATE (this_search_Genre0_create0_node:Genre) SET this_search_Genre0_create0_node.name = $this_search_Genre0_create0_node_name @@ -308,7 +313,7 @@ describe("Cypher Union", () => { input: [ { title: "some movie" - search: { Genre: { connect: [{ where: { node: { name_EQ: "some genre" } } }] } } + search: { Genre: { connect: [{ where: { node: { name: { eq: "some genre" } } } }] } } } ] ) { @@ -323,7 +328,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "CALL { + "CYPHER 5 + CALL { CREATE (this0:Movie) SET this0.title = $this0_title WITH * @@ -338,7 +344,7 @@ describe("Cypher Union", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this0 UNWIND connectedNodes as this0_search_Genre_connect0_node - MERGE (this0)-[:SEARCH]->(this0_search_Genre_connect0_node) + CREATE (this0)-[:SEARCH]->(this0_search_Genre_connect0_node) } } WITH this0, this0_search_Genre_connect0_node @@ -366,11 +372,11 @@ describe("Cypher Union", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "some movie" } + where: { title: { eq: "some movie" } } update: { search: { Genre: { - where: { node: { name_EQ: "some genre" } } + where: { node: { name: { eq: "some genre" } } } update: { node: { name_SET: "some new genre" } } } } @@ -387,7 +393,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { @@ -413,7 +420,9 @@ describe("Cypher Union", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"some genre\\" + \\"name\\": { + \\"eq\\": \\"some genre\\" + } } }, \\"update\\": { @@ -436,8 +445,8 @@ describe("Cypher Union", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "some movie" } - update: { search: { Genre: { disconnect: [{ where: { node: { name_EQ: "some genre" } } }] } } } + where: { title: { eq: "some movie" } } + update: { search: { Genre: { disconnect: [{ where: { node: { name: { eq: "some genre" } } } }] } } } ) { movies { title @@ -450,7 +459,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH this CALL { @@ -482,7 +492,9 @@ describe("Cypher Union", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"some genre\\" + \\"name\\": { + \\"eq\\": \\"some genre\\" + } } } } @@ -502,8 +514,8 @@ describe("Cypher Union", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "some movie" } - update: { search: { Genre: { connect: { where: { node: { name_EQ: "some genre" } } } } } } + where: { title: { eq: "some movie" } } + update: { search: { Genre: { connect: { where: { node: { name: { eq: "some genre" } } } } } } } ) { movies { title @@ -516,7 +528,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { @@ -530,7 +543,7 @@ describe("Cypher Union", () => { WITH connectedNodes, parentNodes UNWIND parentNodes as this UNWIND connectedNodes as this_search_Genre0_connect0_node - MERGE (this)-[:SEARCH]->(this_search_Genre0_connect0_node) + CREATE (this)-[:SEARCH]->(this_search_Genre0_connect0_node) } } WITH this, this_search_Genre0_connect0_node @@ -552,8 +565,8 @@ describe("Cypher Union", () => { const query = /* GraphQL */ ` mutation { updateMovies( - where: { title_EQ: "some movie" } - update: { search: { Genre: { delete: { where: { node: { name_EQ: "some genre" } } } } } } + where: { title: { eq: "some movie" } } + update: { search: { Genre: { delete: { where: { node: { name: { eq: "some genre" } } } } } } } ) { movies { title @@ -566,7 +579,8 @@ describe("Cypher Union", () => { const result = await translateQuery(neoSchema, query, { token }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 WITH * CALL { @@ -597,7 +611,9 @@ describe("Cypher Union", () => { { \\"where\\": { \\"node\\": { - \\"name_EQ\\": \\"some genre\\" + \\"name\\": { + \\"eq\\": \\"some genre\\" + } } } } diff --git a/packages/graphql/tests/tck/where.test.ts b/packages/graphql/tests/tck/where.test.ts index f7311c9532..b04df608f0 100644 --- a/packages/graphql/tests/tck/where.test.ts +++ b/packages/graphql/tests/tck/where.test.ts @@ -47,7 +47,7 @@ describe("Cypher WHERE", () => { test("Simple", async () => { const query = /* GraphQL */ ` query ($title: String, $isFavorite: Boolean) { - movies(where: { title_EQ: $title, isFavorite_EQ: $isFavorite }) { + movies(where: { title: { eq: $title }, isFavorite: { eq: $isFavorite } }) { title } } @@ -58,7 +58,8 @@ describe("Cypher WHERE", () => { }); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (this.title = $param0 AND this.isFavorite = $param1) RETURN this { .title } AS this" `); @@ -74,7 +75,7 @@ describe("Cypher WHERE", () => { test("Simple AND", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ title_EQ: "some title" }] }) { + movies(where: { AND: [{ title: { eq: "some title" } }] }) { title } } @@ -83,7 +84,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -98,7 +100,7 @@ describe("Cypher WHERE", () => { test("Simple AND with multiple parameters", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ title_EQ: "some title" }, { isFavorite_EQ: true }] }) { + movies(where: { AND: [{ title: { eq: "some title" } }, { isFavorite: { eq: true } }] }) { title } } @@ -107,7 +109,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (this.title = $param0 AND this.isFavorite = $param1) RETURN this { .title } AS this" `); @@ -123,7 +126,7 @@ describe("Cypher WHERE", () => { test("Nested AND", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ AND: [{ title_EQ: "some title" }] }] }) { + movies(where: { AND: [{ AND: [{ title: { eq: "some title" } }] }] }) { title } } @@ -132,7 +135,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -147,7 +151,9 @@ describe("Cypher WHERE", () => { test("Nested AND with multiple properties", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ AND: [{ title_EQ: "some title" }, { title_EQ: "another title" }] }] }) { + movies( + where: { AND: [{ AND: [{ title: { eq: "some title" } }, { title: { eq: "another title" } }] }] } + ) { title } } @@ -156,7 +162,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (this.title = $param0 AND this.title = $param1) RETURN this { .title } AS this" `); @@ -172,7 +179,11 @@ describe("Cypher WHERE", () => { test("Nested AND and OR", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ OR: [{ title_EQ: "some title" }, { isFavorite_EQ: true }], id_EQ: 2 }] }) { + movies( + where: { + AND: [{ OR: [{ title: { eq: "some title" } }, { isFavorite: { eq: true } }], id: { eq: 2 } }] + } + ) { title } } @@ -181,7 +192,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE (this.id = $param0 AND (this.title = $param1 OR this.isFavorite = $param2)) RETURN this { .title } AS this" `); @@ -198,7 +210,7 @@ describe("Cypher WHERE", () => { test("Super Nested AND", async () => { const query = /* GraphQL */ ` { - movies(where: { AND: [{ AND: [{ AND: [{ title_EQ: "some title" }] }] }] }) { + movies(where: { AND: [{ AND: [{ AND: [{ title: { eq: "some title" } }] }] }] }) { title } } @@ -207,7 +219,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -222,7 +235,7 @@ describe("Cypher WHERE", () => { test("Simple OR", async () => { const query = /* GraphQL */ ` { - movies(where: { OR: [{ title_EQ: "some title" }] }) { + movies(where: { OR: [{ title: { eq: "some title" } }] }) { title } } @@ -231,7 +244,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -246,7 +260,7 @@ describe("Cypher WHERE", () => { test("Nested OR", async () => { const query = /* GraphQL */ ` { - movies(where: { OR: [{ OR: [{ title_EQ: "some title" }] }] }) { + movies(where: { OR: [{ OR: [{ title: { eq: "some title" } }] }] }) { title } } @@ -255,7 +269,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -270,7 +285,7 @@ describe("Cypher WHERE", () => { test("Super Nested OR", async () => { const query = /* GraphQL */ ` { - movies(where: { OR: [{ OR: [{ OR: [{ title_EQ: "some title" }] }] }] }) { + movies(where: { OR: [{ OR: [{ OR: [{ title: { eq: "some title" } }] }] }] }) { title } } @@ -279,7 +294,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title = $param0 RETURN this { .title } AS this" `); @@ -295,7 +311,7 @@ describe("Cypher WHERE", () => { test("Match with NULL in where", async () => { const query = /* GraphQL */ ` { - movies(where: { title_EQ: null }) { + movies(where: { title: { eq: null } }) { title } } @@ -304,7 +320,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE this.title IS NULL RETURN this { .title } AS this" `); @@ -315,7 +332,7 @@ describe("Cypher WHERE", () => { test("Match with not NULL in where", async () => { const query = /* GraphQL */ ` { - movies(where: { NOT: { title_EQ: null } }) { + movies(where: { NOT: { title: { eq: null } } }) { title } } @@ -324,7 +341,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (this.title IS NULL) RETURN this { .title } AS this" `); @@ -336,7 +354,7 @@ describe("Cypher WHERE", () => { test("Simple NOT", async () => { const query = /* GraphQL */ ` { - movies(where: { NOT: { title_EQ: "some title" } }) { + movies(where: { NOT: { title: { eq: "some title" } } }) { title } } @@ -345,7 +363,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (this.title = $param0) RETURN this { .title } AS this" `); @@ -360,7 +379,7 @@ describe("Cypher WHERE", () => { test("Simple NOT, implicit AND", async () => { const query = /* GraphQL */ ` { - movies(where: { NOT: { title_EQ: "some title", isFavorite_EQ: false } }) { + movies(where: { NOT: { title: { eq: "some title" }, isFavorite: { eq: false } } }) { title } } @@ -369,7 +388,8 @@ describe("Cypher WHERE", () => { const result = await translateQuery(neoSchema, query); expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` - "MATCH (this:Movie) + "CYPHER 5 + MATCH (this:Movie) WHERE NOT (this.title = $param0 AND this.isFavorite = $param1) RETURN this { .title } AS this" `); diff --git a/packages/graphql/tests/utils/builders/driver-builder.ts b/packages/graphql/tests/utils/builders/driver-builder.ts index 097360dfa3..95945c726e 100644 --- a/packages/graphql/tests/utils/builders/driver-builder.ts +++ b/packages/graphql/tests/utils/builders/driver-builder.ts @@ -88,9 +88,9 @@ export class DriverBuilder extends Builder> { const calls: Array = []; function mockFunc(...params) { // this is needed as the first query could be the DB version check query - if (params?.[0] === DBMS_COMPONENTS_QUERY) { + if (params?.[0].includes(DBMS_COMPONENTS_QUERY)) { return { - records: [new Record(["version", "edition"], ["4.0.0", "enterprise"])], + records: [new Record(["version", "edition"], ["5.0.0", "enterprise"])], summary: { counters: { updates() { diff --git a/packages/introspector/CHANGELOG.md b/packages/introspector/CHANGELOG.md index 46d12d0541..725004a743 100644 --- a/packages/introspector/CHANGELOG.md +++ b/packages/introspector/CHANGELOG.md @@ -1,5 +1,15 @@ # @neo4j/introspector +## 5.0.0-alpha.0 + +### Major Changes + +- [#5789](https://github.com/neo4j/graphql/pull/5789) [`1a07d40`](https://github.com/neo4j/graphql/commit/1a07d40888e89c5cd9a40edc16f1742e27bff687) Thanks [@darrellwarde](https://github.com/darrellwarde)! - The Neo4j GraphQL Library and Introspector now required Node.js 22 or greater. + +### Patch Changes + +- [#5837](https://github.com/neo4j/graphql/pull/5837) [`721691a`](https://github.com/neo4j/graphql/commit/721691a84eaa34996c0c97edb7ede1ae4775dd2f) Thanks [@MacondoExpress](https://github.com/MacondoExpress)! - Changed how "@neo4j/introspector" generates list fields that now are generated as a list of non-nullable elements, as a list of nullable elements is not supported by Neo4j. + ## 4.0.1 ### Patch Changes diff --git a/packages/introspector/package.json b/packages/introspector/package.json index 76e90ac50d..ded5f1ff5d 100644 --- a/packages/introspector/package.json +++ b/packages/introspector/package.json @@ -1,6 +1,6 @@ { "name": "@neo4j/introspector", - "version": "4.0.1", + "version": "5.0.0-alpha.0", "description": "Introspect a Neo4j database model/schema", "keywords": [ "neo4j", @@ -22,7 +22,7 @@ "dist/**/*.js.map" ], "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "scripts": { "build:clean": "yarn clean && yarn build", @@ -34,7 +34,7 @@ }, "author": "Neo4j Inc.", "devDependencies": { - "@neo4j/graphql": "^6.2.2", + "@neo4j/graphql": "^7.0.0-alpha.0", "@types/jest": "29.5.14", "@types/node": "22.10.9", "@types/pluralize": "0.0.33", diff --git a/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts b/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts index c7c536370b..10bb3e3add 100644 --- a/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts +++ b/packages/introspector/src/transforms/neo4j-graphql/utils/map-neo4j-to-graphql-type.ts @@ -34,19 +34,19 @@ const map = { Point: "Point", // Array types - LongArray: "[BigInt]", - DoubleArray: "[Float]", - FloatArray: "[Float]", - IntegerArray: "[BigInt]", - BooleanArray: "[Boolean]", - StringArray: "[String]", - DateArray: "[Date]", - DateTimeArray: "[DateTime]", - TimeArray: "[Time]", - LocalTimeArray: "[LocalTime]", - LocalDateTimeArray: "[LocalDateTime]", - DurationArray: "[Duration]", - PointArray: "[Point]", + LongArray: "[BigInt!]", + DoubleArray: "[Float!]", + FloatArray: "[Float!]", + IntegerArray: "[BigInt!]", + BooleanArray: "[Boolean!]", + StringArray: "[String!]", + DateArray: "[Date!]", + DateTimeArray: "[DateTime!]", + TimeArray: "[Time!]", + LocalTimeArray: "[LocalTime!]", + LocalDateTimeArray: "[LocalDateTime!]", + DurationArray: "[Duration!]", + PointArray: "[Point!]", }; export default function mapNeo4jToGraphQLType(neo4jType: string[], mandatory?: boolean): string { diff --git a/packages/introspector/tests/integration/graphql/graphs.test.ts b/packages/introspector/tests/integration/graphql/graphs.test.ts index f9f65eb0dd..0812836ed1 100644 --- a/packages/introspector/tests/integration/graphql/graphs.test.ts +++ b/packages/introspector/tests/integration/graphql/graphs.test.ts @@ -213,7 +213,7 @@ describe("GraphQL - Infer Schema on graphs", () => { expect(typeDefs).toMatchInlineSnapshot(` "type ActedInProperties @relationshipProperties { pay: Float - roles: [String]! + roles: [String!]! } type Actor @node { @@ -269,7 +269,7 @@ describe("GraphQL - Infer Schema on graphs", () => { const typeDefs = await toGraphQLTypeDefs(sessionFactory(bm)); expect(typeDefs).toMatchInlineSnapshot(` "type ActedInProperties @relationshipProperties { - roles: [String]! + roles: [String!]! } type Actor_Label @node(labels: [\\"Actor-Label\\"]) { diff --git a/packages/introspector/tests/integration/graphql/nodes.test.ts b/packages/introspector/tests/integration/graphql/nodes.test.ts index 84dfd8befc..65c720afb2 100644 --- a/packages/introspector/tests/integration/graphql/nodes.test.ts +++ b/packages/introspector/tests/integration/graphql/nodes.test.ts @@ -127,7 +127,7 @@ describe("GraphQL - Infer Schema nodes basic tests", () => { "type TestLabel @node { intProp: BigInt! numberProp: Float! - strArrProp: [String]! + strArrProp: [String!]! strProp: String! }" `); diff --git a/packages/package-tests/apollo/server.js b/packages/package-tests/apollo/server.js index 042a447fcd..9a041c995d 100644 --- a/packages/package-tests/apollo/server.js +++ b/packages/package-tests/apollo/server.js @@ -21,15 +21,15 @@ const { ApolloServer } = require("@apollo/server"); const { startStandaloneServer } = require("@apollo/server/standalone"); const { Neo4jGraphQL } = require("@neo4j/graphql"); -const defaultTypeDefs = ` - type Movie { +const defaultTypeDefs = /* GraphQL */ ` + type Movie @node { title: String year: Int imdbRating: Float genres: [Genre!]! @relationship(type: "IN_GENRE", direction: OUT) } - type Genre { + type Genre @node { name: String movies: [Movie!]! @relationship(type: "IN_GENRE", direction: IN) } diff --git a/packages/package-tests/babel/index.js b/packages/package-tests/babel/index.js index aa13cfe267..015220c1ee 100644 --- a/packages/package-tests/babel/index.js +++ b/packages/package-tests/babel/index.js @@ -22,7 +22,11 @@ import { Neo4jGraphQL } from "@neo4j/graphql"; import { printSchema } from "graphql"; // Augment schema with simple typeDefs input -const typeDefs = `type Movie{ id: ID!}`; +const typeDefs = /* GraphQL */ ` + type Movie @node { + id: ID! + } +`; const neoSchema = new Neo4jGraphQL({ typeDefs }); neoSchema.getSchema().then((schema) => { diff --git a/packages/package-tests/commonjs/test.js b/packages/package-tests/commonjs/test.js index f7845ccbe3..e443167d8f 100644 --- a/packages/package-tests/commonjs/test.js +++ b/packages/package-tests/commonjs/test.js @@ -21,7 +21,11 @@ const { Neo4jGraphQL } = require("@neo4j/graphql"); const { printSchema } = require("graphql"); // Augment schema with simple typeDefs input -const typeDefs = `type Movie{ id: ID!}`; +const typeDefs = /* GraphQL */ ` + type Movie @node { + id: ID! + } +`; const neoSchema = new Neo4jGraphQL({ typeDefs }); neoSchema.getSchema().then((schema) => { diff --git a/packages/package-tests/graphql-15/test.js b/packages/package-tests/graphql-15/test.js index f7845ccbe3..e443167d8f 100644 --- a/packages/package-tests/graphql-15/test.js +++ b/packages/package-tests/graphql-15/test.js @@ -21,7 +21,11 @@ const { Neo4jGraphQL } = require("@neo4j/graphql"); const { printSchema } = require("graphql"); // Augment schema with simple typeDefs input -const typeDefs = `type Movie{ id: ID!}`; +const typeDefs = /* GraphQL */ ` + type Movie @node { + id: ID! + } +`; const neoSchema = new Neo4jGraphQL({ typeDefs }); neoSchema.getSchema().then((schema) => { diff --git a/packages/package-tests/typescript/src/test.ts b/packages/package-tests/typescript/src/test.ts index 26cbade8d6..7da06eab9c 100644 --- a/packages/package-tests/typescript/src/test.ts +++ b/packages/package-tests/typescript/src/test.ts @@ -21,7 +21,11 @@ import { Neo4jGraphQL } from "@neo4j/graphql"; import { printSchema } from "graphql"; // Augment schema with simple typeDefs input -const typeDefs = `type Movie{ id: ID!}`; +const typeDefs = /* GraphQL */ ` + type Movie @node { + id: ID! + } +`; const neoSchema = new Neo4jGraphQL({ typeDefs }); void neoSchema.getSchema().then((schema) => { diff --git a/yarn.lock b/yarn.lock index 3112376040..87afdf5e62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2136,7 +2136,7 @@ __metadata: languageName: node linkType: soft -"@neo4j/graphql@npm:^6.2.2, @neo4j/graphql@npm:^6.3.1, @neo4j/graphql@workspace:packages/graphql": +"@neo4j/graphql@npm:^7.0.0-alpha.0, @neo4j/graphql@npm:^7.0.0-alpha.3, @neo4j/graphql@workspace:packages/graphql": version: 0.0.0-use.local resolution: "@neo4j/graphql@workspace:packages/graphql" dependencies: @@ -2164,6 +2164,7 @@ __metadata: graphql-compose: "npm:^9.0.8" graphql-middleware: "npm:6.1.35" graphql-parse-resolve-info: "npm:^4.12.3" + graphql-query-complexity: "npm:^1.0.0" graphql-relay: "npm:^0.10.0" graphql-tag: "npm:2.12.6" graphql-ws: "npm:5.16.2" @@ -2197,7 +2198,7 @@ __metadata: version: 0.0.0-use.local resolution: "@neo4j/introspector@workspace:packages/introspector" dependencies: - "@neo4j/graphql": "npm:^6.2.2" + "@neo4j/graphql": "npm:^7.0.0-alpha.0" "@types/jest": "npm:29.5.14" "@types/node": "npm:22.10.9" "@types/pluralize": "npm:0.0.33" @@ -3887,7 +3888,7 @@ __metadata: "@apollo/federation-subgraph-compatibility": "npm:2.2.0" "@apollo/server": "npm:^4.7.0" "@graphql-tools/wrap": "npm:^10.0.0" - "@neo4j/graphql": "npm:^6.3.1" + "@neo4j/graphql": "npm:^7.0.0-alpha.3" fork-ts-checker-webpack-plugin: "npm:9.0.2" graphql: "npm:16.10.0" graphql-tag: "npm:^2.12.6" @@ -8696,6 +8697,17 @@ __metadata: languageName: node linkType: hard +"graphql-query-complexity@npm:^1.0.0": + version: 1.0.0 + resolution: "graphql-query-complexity@npm:1.0.0" + dependencies: + lodash.get: "npm:^4.4.2" + peerDependencies: + graphql: ^14.6.0 || ^15.0.0 || ^16.0.0 + checksum: 10c0/94b9e35eb6d504464cac25a077dc403b3c94666874748451b9175027e790216d8b52765f1d7b6a73bef80d8e0ba3a7fad52bf281080bf12be976f7edf1a3e5da + languageName: node + linkType: hard + "graphql-relay@npm:^0.10.0": version: 0.10.2 resolution: "graphql-relay@npm:0.10.2" @@ -11280,6 +11292,13 @@ __metadata: languageName: node linkType: hard +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: 10c0/48f40d471a1654397ed41685495acb31498d5ed696185ac8973daef424a749ca0c7871bf7b665d5c14f5cc479394479e0307e781f61d5573831769593411be6e + languageName: node + linkType: hard + "lodash.includes@npm:^4.3.0": version: 4.3.0 resolution: "lodash.includes@npm:4.3.0"